diff options
author | Mark Shannon <mark@hotpy.org> | 2021-11-16 11:01:57 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-16 11:01:57 +0000 |
commit | b9310773756f40f77e075f221a90dd41e6964efc (patch) | |
tree | 43674ba9f42f2ff81dfd3025956b0ce8e12251f9 /Python | |
parent | 9bf2cbc4c498812e14f20d86acb61c53928a5a57 (diff) | |
download | cpython3-b9310773756f40f77e075f221a90dd41e6964efc.tar.gz |
bpo-45753: Make recursion checks more efficient. (GH-29524)
* Uses recursion remaining, instead of recursion depth to speed up check against recursion limit.
Diffstat (limited to 'Python')
-rw-r--r-- | Python/ast.c | 5 | ||||
-rw-r--r-- | Python/ast_opt.c | 5 | ||||
-rw-r--r-- | Python/ceval.c | 41 | ||||
-rw-r--r-- | Python/pystate.c | 4 | ||||
-rw-r--r-- | Python/symtable.c | 5 | ||||
-rw-r--r-- | Python/sysmodule.c | 16 |
6 files changed, 40 insertions, 36 deletions
diff --git a/Python/ast.c b/Python/ast.c index 2113124dbd..0c3121d3ee 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -933,8 +933,9 @@ _PyAST_Validate(mod_ty mod) return 0; } /* Be careful here to prevent overflow. */ - starting_recursion_depth = (tstate->recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - tstate->recursion_depth * COMPILER_STACK_FRAME_SCALE : tstate->recursion_depth; + int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; + starting_recursion_depth = (recursion_depth< INT_MAX / COMPILER_STACK_FRAME_SCALE) ? + recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; state.recursion_depth = starting_recursion_depth; state.recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; diff --git a/Python/ast_opt.c b/Python/ast_opt.c index f6506cef03..356f60e2d5 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -1098,8 +1098,9 @@ _PyAST_Optimize(mod_ty mod, PyArena *arena, _PyASTOptimizeState *state) return 0; } /* Be careful here to prevent overflow. */ - starting_recursion_depth = (tstate->recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - tstate->recursion_depth * COMPILER_STACK_FRAME_SCALE : tstate->recursion_depth; + int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; + starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? + recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; state->recursion_depth = starting_recursion_depth; state->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; diff --git a/Python/ceval.c b/Python/ceval.c index e808aeed73..bf4e22dc6f 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -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; } diff --git a/Python/pystate.c b/Python/pystate.c index a9ed08a7e3..8df28078f2 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -636,9 +636,9 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->interp = interp; - tstate->recursion_depth = 0; + tstate->recursion_limit = interp->ceval.recursion_limit; + tstate->recursion_remaining = interp->ceval.recursion_limit; tstate->recursion_headroom = 0; - tstate->stackcheck_counter = 0; tstate->tracing = 0; tstate->root_cframe.use_tracing = 0; tstate->root_cframe.current_frame = NULL; diff --git a/Python/symtable.c b/Python/symtable.c index 64c1635fba..dc5426cf3b 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -298,8 +298,9 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future) return NULL; } /* Be careful here to prevent overflow. */ - starting_recursion_depth = (tstate->recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? - tstate->recursion_depth * COMPILER_STACK_FRAME_SCALE : tstate->recursion_depth; + int recursion_depth = tstate->recursion_limit - tstate->recursion_remaining; + starting_recursion_depth = (recursion_depth < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? + recursion_depth * COMPILER_STACK_FRAME_SCALE : recursion_depth; st->recursion_depth = starting_recursion_depth; st->recursion_limit = (recursion_limit < INT_MAX / COMPILER_STACK_FRAME_SCALE) ? recursion_limit * COMPILER_STACK_FRAME_SCALE : recursion_limit; diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 27937a03e8..3e2091e70a 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1187,20 +1187,14 @@ sys_setrecursionlimit_impl(PyObject *module, int new_limit) return NULL; } - /* Issue #25274: When the recursion depth hits the recursion limit in - _Py_CheckRecursiveCall(), the overflowed flag of the thread state is - set to 1 and a RecursionError is raised. The overflowed flag is reset - to 0 when the recursion depth goes below the low-water mark: see - Py_LeaveRecursiveCall(). - - Reject too low new limit if the current recursion depth is higher than - the new low-water mark. Otherwise it may not be possible anymore to - reset the overflowed flag to 0. */ - if (tstate->recursion_depth >= new_limit) { + /* Reject too low new limit if the current recursion depth is higher than + the new low-water mark. */ + int depth = tstate->recursion_limit - tstate->recursion_remaining; + if (depth >= new_limit) { _PyErr_Format(tstate, PyExc_RecursionError, "cannot set the recursion limit to %i at " "the recursion depth %i: the limit is too low", - new_limit, tstate->recursion_depth); + new_limit, depth); return NULL; } |