From: Jiucheng(Oliver) Date: Fri, 24 Jul 2026 04:41:08 +0000 (-0400) Subject: gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state... X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6e983938834a8b6c7043369b3c5de2b5d2084ea4;p=thirdparty%2FPython%2Fcpython.git gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state (#153307) Co-authored-by: Kumar Aditya Co-authored-by: Xiaowei Lu --- 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 000000000000..a9a432fae16b --- /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 166397a72da3..e9950bb23243 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); }