Skip to content

Commit

Permalink
[3.13] gh-128717: Stop-the-world when setting the recursion limit (GH…
Browse files Browse the repository at this point in the history
…-128741) (#128757)

[3.13] gh-128717: Stop-the-world when setting the recursion limit (GH-128741)
(cherry picked from commit f6c61bf)

Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
  • Loading branch information
ZeroIntensity authored Jan 14, 2025
1 parent f2a2809 commit 1ec36a6
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 0 deletions.
30 changes: 30 additions & 0 deletions Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix a crash when setting the recursion limit while other threads are active
on the :term:`free threaded <free threading>` build.
4 changes: 4 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down

0 comments on commit 1ec36a6

Please sign in to comment.