https://github.com/python/cpython/commit/6e983938834a8b6c7043369b3c5de2b5d2084ea4 commit: 6e983938834a8b6c7043369b3c5de2b5d2084ea4 branch: main author: Jiucheng(Oliver) <[email protected]> committer: kumaraditya303 <[email protected]> date: 2026-07-24T10:11:08+05:30 summary:
gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state (#153307) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Xiaowei Lu <[email protected]> files: A Lib/test/test_free_threading/test_interpreters.py M Modules/_testinternalcapi.c diff --git a/Lib/test/test_free_threading/test_interpreters.py b/Lib/test/test_free_threading/test_interpreters.py new file mode 100644 index 00000000000000..a9a432fae16b1e --- /dev/null +++ b/Lib/test/test_free_threading/test_interpreters.py @@ -0,0 +1,29 @@ +import textwrap +import unittest + +from test.support import import_helper, script_helper + + +# Make sure _testinternalcapi is available before running the test. +import_helper.import_module('_testinternalcapi') + + +class InterpreterTeardownTests(unittest.TestCase): + def test_destroy_subinterpreter_does_not_abort(self): + # gh-153176: destroy_interpreter(basic=True) used to call + # PyThreadState_Clear() on a non-current thread state, which on a + # free-threaded debug build reclaimed mimalloc pages into a heap not + # owned by the current thread and aborted the process. Run the + # reproduction in a subprocess so that a regression surfaces as a + # non-zero exit / SIGABRT instead of killing the test runner. + script = textwrap.dedent(""" + import _testinternalcapi + + interpid = _testinternalcapi.create_interpreter() + _testinternalcapi.destroy_interpreter(interpid, basic=True) + """) + script_helper.assert_python_ok('-c', script) + + +if __name__ == "__main__": + unittest.main() diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 166397a72da3aa..e9950bb232431c 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -2312,8 +2312,7 @@ destroy_interpreter(PyObject *self, PyObject *args, PyObject *kwargs) } t2 = PyThreadState_New(interp); prev = PyThreadState_Swap(t2); - PyThreadState_Clear(t1); - PyThreadState_Delete(t1); + // t1 is deliberately left alive; Py_EndInterpreter() must clean it up. Py_EndInterpreter(t2); PyThreadState_Swap(prev); } _______________________________________________ Python-checkins mailing list -- [email protected] To unsubscribe send an email to [email protected] https://mail.python.org/mailman3//lists/python-checkins.python.org Member address: [email protected]
