Skip to content

Commit 4e30ed3

Browse files
authored
bpo-40513: Per-interpreter recursion_limit (GH-19929)
Move recursion_limit member from _PyRuntimeState.ceval to PyInterpreterState.ceval. * Py_SetRecursionLimit() now only sets _Py_CheckRecursionLimit of ceval.c if the current Python thread is part of the main interpreter. * Inline _Py_MakeEndRecCheck() into _Py_LeaveRecursiveCall(). * Convert _Py_RecursionLimitLowerWaterMark() macro into a static inline function.
1 parent 627f701 commit 4e30ed3

File tree

4 files changed

+28
-22
lines changed

4 files changed

+28
-22
lines changed

Include/internal/pycore_ceval.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ PyAPI_DATA(int) _Py_CheckRecursionLimit;
6565
/* With USE_STACKCHECK macro defined, trigger stack checks in
6666
_Py_CheckRecursiveCall() on every 64th call to Py_EnterRecursiveCall. */
6767
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
68-
return (++tstate->recursion_depth > _Py_CheckRecursionLimit
68+
return (++tstate->recursion_depth > tstate->interp->ceval.recursion_limit
6969
|| ++tstate->stackcheck_counter > 64);
7070
}
7171
#else
7272
static inline int _Py_MakeRecCheck(PyThreadState *tstate) {
73-
return (++tstate->recursion_depth > _Py_CheckRecursionLimit);
73+
return (++tstate->recursion_depth > tstate->interp->ceval.recursion_limit);
7474
}
7575
#endif
7676

@@ -90,20 +90,22 @@ static inline int _Py_EnterRecursiveCall_inline(const char *where) {
9090

9191
#define Py_EnterRecursiveCall(where) _Py_EnterRecursiveCall_inline(where)
9292

93-
9493
/* Compute the "lower-water mark" for a recursion limit. When
9594
* Py_LeaveRecursiveCall() is called with a recursion depth below this mark,
9695
* the overflowed flag is reset to 0. */
97-
#define _Py_RecursionLimitLowerWaterMark(limit) \
98-
(((limit) > 200) \
99-
? ((limit) - 50) \
100-
: (3 * ((limit) >> 2)))
101-
102-
#define _Py_MakeEndRecCheck(x) \
103-
(--(x) < _Py_RecursionLimitLowerWaterMark(_Py_CheckRecursionLimit))
96+
static inline int _Py_RecursionLimitLowerWaterMark(int limit) {
97+
if (limit > 200) {
98+
return (limit - 50);
99+
}
100+
else {
101+
return (3 * (limit >> 2));
102+
}
103+
}
104104

105105
static inline void _Py_LeaveRecursiveCall(PyThreadState *tstate) {
106-
if (_Py_MakeEndRecCheck(tstate->recursion_depth)) {
106+
tstate->recursion_depth--;
107+
int limit = tstate->interp->ceval.recursion_limit;
108+
if (tstate->recursion_depth < _Py_RecursionLimitLowerWaterMark(limit)) {
107109
tstate->overflowed = 0;
108110
}
109111
}

Include/internal/pycore_interp.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ struct _pending_calls {
3333
};
3434

3535
struct _ceval_state {
36+
int recursion_limit;
3637
/* Records whether tracing is on for any thread. Counts the number
3738
of threads for which tstate->c_tracefunc is non-NULL, so if the
3839
value is 0, we know we don't have to check this thread's

Include/internal/pycore_runtime.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ extern "C" {
1414
/* ceval state */
1515

1616
struct _ceval_runtime_state {
17-
int recursion_limit;
1817
struct _gil_runtime_state gil;
1918
};
2019

Python/ceval.c

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -699,14 +699,15 @@ int _Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
699699
void
700700
_PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
701701
{
702-
ceval->recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
703702
_Py_CheckRecursionLimit = Py_DEFAULT_RECURSION_LIMIT;
704703
_gil_initialize(&ceval->gil);
705704
}
706705

707706
int
708707
_PyEval_InitState(struct _ceval_state *ceval)
709708
{
709+
ceval->recursion_limit = Py_DEFAULT_RECURSION_LIMIT;
710+
710711
struct _pending_calls *pending = &ceval->pending;
711712
assert(pending->lock == NULL);
712713

@@ -730,16 +731,18 @@ _PyEval_FiniState(struct _ceval_state *ceval)
730731
int
731732
Py_GetRecursionLimit(void)
732733
{
733-
struct _ceval_runtime_state *ceval = &_PyRuntime.ceval;
734-
return ceval->recursion_limit;
734+
PyThreadState *tstate = _PyThreadState_GET();
735+
return tstate->interp->ceval.recursion_limit;
735736
}
736737

737738
void
738739
Py_SetRecursionLimit(int new_limit)
739740
{
740-
struct _ceval_runtime_state *ceval = &_PyRuntime.ceval;
741-
ceval->recursion_limit = new_limit;
742-
_Py_CheckRecursionLimit = new_limit;
741+
PyThreadState *tstate = _PyThreadState_GET();
742+
tstate->interp->ceval.recursion_limit = new_limit;
743+
if (_Py_IsMainInterpreter(tstate)) {
744+
_Py_CheckRecursionLimit = new_limit;
745+
}
743746
}
744747

745748
/* The function _Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall()
@@ -750,8 +753,7 @@ Py_SetRecursionLimit(int new_limit)
750753
int
751754
_Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
752755
{
753-
_PyRuntimeState *runtime = tstate->interp->runtime;
754-
int recursion_limit = runtime->ceval.recursion_limit;
756+
int recursion_limit = tstate->interp->ceval.recursion_limit;
755757

756758
#ifdef USE_STACKCHECK
757759
tstate->stackcheck_counter = 0;
@@ -760,8 +762,10 @@ _Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
760762
_PyErr_SetString(tstate, PyExc_MemoryError, "Stack overflow");
761763
return -1;
762764
}
763-
/* Needed for ABI backwards-compatibility (see bpo-31857) */
764-
_Py_CheckRecursionLimit = recursion_limit;
765+
if (_Py_IsMainInterpreter(tstate)) {
766+
/* Needed for ABI backwards-compatibility (see bpo-31857) */
767+
_Py_CheckRecursionLimit = recursion_limit;
768+
}
765769
#endif
766770
if (tstate->recursion_critical)
767771
/* Somebody asked that we don't check for recursion. */

0 commit comments

Comments
 (0)