]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-153176: Fix destroy_interpreter() test helper clearing a non-current thread state...
authorJiucheng(Oliver) <git.jiucheng@gmail.com>
Fri, 24 Jul 2026 04:41:08 +0000 (00:41 -0400)
committerGitHub <noreply@github.com>
Fri, 24 Jul 2026 04:41:08 +0000 (10:11 +0530)
Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Co-authored-by: Xiaowei Lu <weixlu420302@gmail.com>
Lib/test/test_free_threading/test_interpreters.py [new file with mode: 0644]
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 (file)
index 0000000..a9a432f
--- /dev/null
@@ -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()
index 166397a72da3aa70d71b5466758fc4d1e536e66a..e9950bb232431c69d888bac489bdb67294079edc 100644 (file)
@@ -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);
     }