Skip to content

Commit 1896793

Browse files
pdoxbenjaminp
authored andcommitted
bpo-31857: Make the behavior of USE_STACKCHECK deterministic (python#4098)
1 parent 3231893 commit 1896793

File tree

6 files changed

+15
-11
lines changed

6 files changed

+15
-11
lines changed

Include/ceval.h

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -93,21 +93,19 @@ PyAPI_FUNC(int) Py_GetRecursionLimit(void);
9393
PyThreadState_GET()->overflowed = 0; \
9494
} while(0)
9595
PyAPI_FUNC(int) _Py_CheckRecursiveCall(const char *where);
96-
/* XXX _Py_CheckRecursionLimit should be changed to
97-
_PyRuntime.ceval.check_recursion_limit. However, due to the macros
98-
in which it's used, _Py_CheckRecursionLimit is stuck in the stable
99-
ABI. It should be removed therefrom when possible.
96+
97+
/* Due to the macros in which it's used, _Py_CheckRecursionLimit is in
98+
the stable ABI. It should be removed therefrom when possible.
10099
*/
101100
PyAPI_DATA(int) _Py_CheckRecursionLimit;
102101

103102
#ifdef USE_STACKCHECK
104-
/* With USE_STACKCHECK, we artificially decrement the recursion limit in order
105-
to trigger regular stack checks in _Py_CheckRecursiveCall(), except if
106-
the "overflowed" flag is set, in which case we need the true value
107-
of _Py_CheckRecursionLimit for _Py_MakeEndRecCheck() to function properly.
103+
/* With USE_STACKCHECK, trigger stack checks in _Py_CheckRecursiveCall()
104+
on every 64th call to Py_EnterRecursiveCall.
108105
*/
109106
# define _Py_MakeRecCheck(x) \
110-
(++(x) > (_Py_CheckRecursionLimit += PyThreadState_GET()->overflowed - 1))
107+
(++(x) > _Py_CheckRecursionLimit || \
108+
++(PyThreadState_GET()->stackcheck_counter) > 64)
111109
#else
112110
# define _Py_MakeRecCheck(x) (++(x) > _Py_CheckRecursionLimit)
113111
#endif

Include/internal/ceval.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ struct _pending_calls {
2929

3030
struct _ceval_runtime_state {
3131
int recursion_limit;
32-
int check_recursion_limit;
3332
/* Records whether tracing is on for any thread. Counts the number
3433
of threads for which tstate->c_tracefunc is non-NULL, so if the
3534
value is 0, we know we don't have to check this thread's

Include/pystate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ typedef struct _ts {
151151
to handle the runtime error. */
152152
char recursion_critical; /* The current calls must not cause
153153
a stack overflow. */
154+
int stackcheck_counter;
155+
154156
/* 'tracing' keeps track of the execution depth when tracing/profiling.
155157
This is to prevent the actual trace/profile code from being recorded in
156158
the trace/profile. */
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Make the behavior of USE_STACKCHECK deterministic in a multi-threaded
2+
environment.

Python/ceval.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,13 +469,15 @@ _Py_CheckRecursiveCall(const char *where)
469469
int recursion_limit = _PyRuntime.ceval.recursion_limit;
470470

471471
#ifdef USE_STACKCHECK
472+
tstate->stackcheck_counter = 0;
472473
if (PyOS_CheckStack()) {
473474
--tstate->recursion_depth;
474475
PyErr_SetString(PyExc_MemoryError, "Stack overflow");
475476
return -1;
476477
}
477-
#endif
478+
/* Needed for ABI backwards-compatibility (see bpo-31857) */
478479
_Py_CheckRecursionLimit = recursion_limit;
480+
#endif
479481
if (tstate->recursion_critical)
480482
/* Somebody asked that we don't check for recursion. */
481483
return 0;

Python/pystate.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ new_threadstate(PyInterpreterState *interp, int init)
245245
tstate->recursion_depth = 0;
246246
tstate->overflowed = 0;
247247
tstate->recursion_critical = 0;
248+
tstate->stackcheck_counter = 0;
248249
tstate->tracing = 0;
249250
tstate->use_tracing = 0;
250251
tstate->gilstate_counter = 0;

0 commit comments

Comments
 (0)