diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 400577d36cd44d..01ce0118651b75 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -362,6 +362,36 @@ def test_setrecursionlimit_to_depth(self): finally: sys.setrecursionlimit(old_limit) + @unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful if the GIL is disabled") + @threading_helper.requires_working_threading() + def test_racing_recursion_limit(self): + from threading import Thread + def something_recursive(): + def count(n): + if n > 0: + return count(n - 1) + 1 + return 0 + + count(50) + + def set_recursion_limit(): + for limit in range(100, 200): + sys.setrecursionlimit(limit) + + threads = [] + for _ in range(5): + threads.append(Thread(target=set_recursion_limit)) + + for _ in range(5): + threads.append(Thread(target=something_recursive)) + + with threading_helper.catch_threading_exception() as cm: + with threading_helper.start_threads(threads): + pass + + if cm.exc_value: + raise cm.exc_value + def test_getwindowsversion(self): # Raise SkipTest if sys doesn't have getwindowsversion attribute test.support.get_attribute(sys, "getwindowsversion") diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst new file mode 100644 index 00000000000000..212c6d3cb97216 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-11-12-39-17.gh-issue-128717.i65d06.rst @@ -0,0 +1,2 @@ +Fix a crash when setting the recursion limit while other threads are active +on the :term:`free threaded ` build. diff --git a/Python/ceval.c b/Python/ceval.c index 04dd0840519c37..2f67d40874ba41 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -265,12 +265,16 @@ void Py_SetRecursionLimit(int new_limit) { PyInterpreterState *interp = _PyInterpreterState_GET(); + _PyEval_StopTheWorld(interp); + HEAD_LOCK(interp->runtime); interp->ceval.recursion_limit = new_limit; for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) { int depth = p->py_recursion_limit - p->py_recursion_remaining; p->py_recursion_limit = new_limit; p->py_recursion_remaining = new_limit - depth; } + HEAD_UNLOCK(interp->runtime); + _PyEval_StartTheWorld(interp); } /* The function _Py_EnterRecursiveCallTstate() only calls _Py_CheckRecursiveCall()