bpo-45753: Make recursion checks more efficient. (GH-29524)

* Uses recursion remaining, instead of recursion depth to speed up check against recursion limit.
This commit is contained in:
Mark Shannon 2021-11-16 11:01:57 +00:00 committed by GitHub
parent 9bf2cbc4c4
commit b931077375
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 50 additions and 43 deletions

View file

@ -785,42 +785,49 @@ Py_GetRecursionLimit(void)
void
Py_SetRecursionLimit(int new_limit)
{
PyThreadState *tstate = _PyThreadState_GET();
tstate->interp->ceval.recursion_limit = new_limit;
PyInterpreterState *interp = _PyInterpreterState_GET();
interp->ceval.recursion_limit = new_limit;
for (PyThreadState *p = interp->tstate_head; p != NULL; p = p->next) {
int depth = p->recursion_limit - p->recursion_remaining;
p->recursion_limit = new_limit;
p->recursion_remaining = new_limit - depth;
}
}
/* The function _Py_EnterRecursiveCall() only calls _Py_CheckRecursiveCall()
if the recursion_depth reaches recursion_limit.
If USE_STACKCHECK, the macro decrements recursion_limit
to guarantee that _Py_CheckRecursiveCall() is regularly called.
Without USE_STACKCHECK, there is no need for this. */
if the recursion_depth reaches recursion_limit. */
int
_Py_CheckRecursiveCall(PyThreadState *tstate, const char *where)
{
int recursion_limit = tstate->interp->ceval.recursion_limit;
/* Check against global limit first. */
int depth = tstate->recursion_limit - tstate->recursion_remaining;
if (depth < tstate->interp->ceval.recursion_limit) {
tstate->recursion_limit = tstate->interp->ceval.recursion_limit;
tstate->recursion_remaining = tstate->recursion_limit - depth;
assert(tstate->recursion_remaining > 0);
return 0;
}
#ifdef USE_STACKCHECK
tstate->stackcheck_counter = 0;
if (PyOS_CheckStack()) {
--tstate->recursion_depth;
++tstate->recursion_remaining;
_PyErr_SetString(tstate, PyExc_MemoryError, "Stack overflow");
return -1;
}
#endif
if (tstate->recursion_headroom) {
if (tstate->recursion_depth > recursion_limit + 50) {
if (tstate->recursion_remaining < -50) {
/* Overflowing while handling an overflow. Give up. */
Py_FatalError("Cannot recover from stack overflow.");
}
}
else {
if (tstate->recursion_depth > recursion_limit) {
if (tstate->recursion_remaining <= 0) {
tstate->recursion_headroom++;
_PyErr_Format(tstate, PyExc_RecursionError,
"maximum recursion depth exceeded%s",
where);
tstate->recursion_headroom--;
--tstate->recursion_depth;
++tstate->recursion_remaining;
return -1;
}
}
@ -1582,7 +1589,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, InterpreterFrame *frame, int thr
start_frame:
if (_Py_EnterRecursiveCall(tstate, "")) {
tstate->recursion_depth++;
tstate->recursion_remaining--;
goto exit_eval_frame;
}
@ -5688,13 +5695,13 @@ fail:
static int
_PyEvalFrameClearAndPop(PyThreadState *tstate, InterpreterFrame * frame)
{
++tstate->recursion_depth;
--tstate->recursion_remaining;
assert(frame->frame_obj == NULL || frame->frame_obj->f_own_locals_memory == 0);
if (_PyFrame_Clear(frame, 0)) {
--tstate->recursion_depth;
++tstate->recursion_remaining;
return -1;
}
--tstate->recursion_depth;
++tstate->recursion_remaining;
_PyThreadState_PopFrame(tstate, frame);
return 0;
}