import functools
import platform
import sys
+import textwrap
import unittest
import weakref
import tkinter
import enum
from test import support
from test.support import os_helper
+from test.support.script_helper import assert_python_ok
from test.test_tkinter.support import setUpModule # noqa: F401
from test.test_tkinter.support import (AbstractTkTest, AbstractDefaultRootTest,
requires_tk, get_tk_patchlevel,
b4 = Button2(f2)
self.assertEqual(len({str(b), str(b2), str(b3), str(b4)}), 4)
+ def test_dealloc_in_wrong_thread(self):
+ # gh-83274: deallocating the interpreter in the wrong thread must not
+ # crash.
+ script = textwrap.dedent("""
+ import threading
+ import tkinter
+ root = tkinter.Tk()
+ root.destroy()
+ # Let another thread drop the last reference.
+ ready = threading.Event()
+ t = threading.Thread(target=lambda obj: ready.wait(), args=(root,))
+ t.start()
+ del root
+ ready.set()
+ t.join()
+ print('ok')
+ """)
+ rc, out, err = assert_python_ok('-c', script)
+ self.assertEqual(out.strip(), b'ok')
+ if not support.Py_GIL_DISABLED:
+ # On the free-threaded build the interpreter may instead be
+ # deallocated in its own thread (deferred reference counting), so
+ # the warning is not necessarily emitted. The crucial guarantee --
+ # no crash -- is already checked by assert_python_ok() above.
+ self.assertIn(b'RuntimeWarning', err)
+ self.assertIn(b'gh-83274', err)
+
@requires_tk(8, 6, 6)
def test_tk_busy(self):
root = self.root
{
TkappObject *self = TkappObject_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
- /*CHECK_TCL_APPARTMENT;*/
- ENTER_TCL
- Tcl_DeleteInterp(Tkapp_Interp(self));
- LEAVE_TCL
+ if (self->threaded && self->thread_id != Tcl_GetCurrentThread()) {
+ /* Deleting the interpreter from another thread aborts the process
+ ("Tcl_AsyncDelete: async handler deleted by the wrong thread").
+ Leak it instead (gh-83274). */
+ if (PyErr_WarnEx(PyExc_RuntimeWarning,
+ "the Tcl interpreter is leaked because it was "
+ "deallocated in a thread other than the one it was "
+ "created in (see gh-83274)", 1) < 0)
+ {
+ PyErr_FormatUnraisable("Exception ignored while finalizing "
+ "a Tcl interpreter");
+ }
+ }
+ else {
+ ENTER_TCL
+ Tcl_DeleteInterp(Tkapp_Interp(self));
+ LEAVE_TCL
+ }
Py_XDECREF(self->trace);
PyObject_Free(self);
Py_DECREF(tp);