We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
1 parent 0a160bf commit f2de1e6Copy full SHA for f2de1e6
Lib/test/test_interpreters/test_api.py
@@ -1452,6 +1452,14 @@ def test_destroy(self):
1452
self.assertFalse(
1453
self.interp_exists(interpid))
1454
1455
+ with self.subTest('basic C-API'):
1456
+ interpid = _testinternalcapi.create_interpreter()
1457
+ self.assertTrue(
1458
+ self.interp_exists(interpid))
1459
+ _testinternalcapi.destroy_interpreter(interpid, basic=True)
1460
+ self.assertFalse(
1461
1462
+
1463
def test_get_config(self):
1464
# This test overlaps with
1465
# test.test_capi.test_misc.InterpreterConfigTests.
Misc/NEWS.d/next/C_API/2025-05-17-14-41-21.gh-issue-134144.xVpZik.rst
@@ -0,0 +1 @@
1
+Fix crash when calling :c:func:`Py_EndInterpreter` with a :term:`thread state` that isn't the initial thread for the interpreter.
Modules/_testinternalcapi.c
@@ -1690,11 +1690,12 @@ create_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
1690
static PyObject *
1691
destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
1692
{
1693
- static char *kwlist[] = {"id", NULL};
+ static char *kwlist[] = {"id", "basic", NULL};
1694
PyObject *idobj = NULL;
1695
+ int basic = 0;
1696
if (!PyArg_ParseTupleAndKeywords(args, kwargs,
- "O:destroy_interpreter", kwlist,
1697
- &idobj))
+ "O|p:destroy_interpreter", kwlist,
1698
+ &idobj, &basic))
1699
1700
return NULL;
1701
}
@@ -1704,7 +1705,27 @@ destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs)
1704
1705
1706
1707
- _PyXI_EndInterpreter(interp, NULL);
1708
+ if (basic)
1709
+ {
1710
+ / Test the basic Py_EndInterpreter with weird out of order thread states
1711
+ PyThreadState *t1, *t2;
1712
+ PyThreadState *prev;
1713
+ t1 = interp->threads.head;
1714
+ if (t1 == NULL) {
1715
+ t1 = PyThreadState_New(interp);
1716
+ }
1717
+ t2 = PyThreadState_New(interp);
1718
+ prev = PyThreadState_Swap(t2);
1719
+ PyThreadState_Clear(t1);
1720
+ PyThreadState_Delete(t1);
1721
+ Py_EndInterpreter(t2);
1722
+ PyThreadState_Swap(prev);
1723
1724
+ else
1725
1726
+ / use the cross interpreter _PyXI_EndInterpreter normally
1727
+ _PyXI_EndInterpreter(interp, NULL);
1728
1729
Py_RETURN_NONE;
1730
1731
Python/pystate.c
@@ -1908,9 +1908,14 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
1908
static void
1909
zapthreads(PyInterpreterState *interp)
1910
1911
+ PyThreadState *tstate;
1912
/* No need to lock the mutex here because this should only happen
- when the threads are all really dead (XXX famous last words). */
1913
- _Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) {
+ when the threads are all really dead (XXX famous last words).
1914
1915
+ Cannot use _Py_FOR_EACH_TSTATE_UNLOCKED because we are freeing
1916
+ the thread states here.
1917
+ */
1918
+ while ((tstate = interp->threads.head) != NULL) {
1919
tstate_verify_not_active(tstate);
1920
tstate_delete_common(tstate, 0);
1921
free_threadstate((_PyThreadStateImpl *)tstate);