diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index bcb1bb25a49..a35e5b803bd 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -53,12 +53,19 @@ typedef struct _cframe { } CFrame; typedef struct _err_stackitem { - /* This struct represents an entry on the exception stack, which is a - * per-coroutine state. (Coroutine in the computer science sense, - * including the thread and generators). - * This ensures that the exception state is not impacted by "yields" - * from an except handler. + /* This struct represents a single execution context where we might + * be currently handling an exception. It is a per-coroutine state + * (coroutine in the computer science sense, including the thread + * and generators). + * + * This is used as an entry on the exception stack, where each + * entry indicates if it is currently handling an exception. + * This ensures that the exception state is not impacted + * by "yields" from an except handler. The thread + * always has an entry (the bottom-most one). */ + + /* The exception currently being handled in this context, if any. */ PyObject *exc_value; struct _err_stackitem *previous_item; @@ -112,13 +119,9 @@ struct _ts { PyObject *curexc_value; PyObject *curexc_traceback; - /* The exception currently being handled, if no coroutines/generators - * are present. Always last element on the stack referred to be exc_info. - */ - _PyErr_StackItem exc_state; - - /* Pointer to the top of the stack of the exceptions currently - * being handled */ + /* Pointer to the top of the exception stack for the exceptions + * we may be currently handling. (See _PyErr_StackItem above.) + * This is never NULL. */ _PyErr_StackItem *exc_info; PyObject *dict; /* Stores per-thread state */ @@ -174,13 +177,26 @@ struct _ts { /* Unique thread state id. */ uint64_t id; - CFrame root_cframe; PyTraceInfo trace_info; _PyStackChunk *datastack_chunk; PyObject **datastack_top; PyObject **datastack_limit; /* XXX signal handlers should also be here */ + + /* The following fields are here to avoid allocation during init. + The data is exposed through PyThreadState pointer fields. + These fields should not be accessed directly outside of init. + + All other PyInterpreterState pointer fields are populated when + needed and default to NULL. + */ + + /* The thread's exception stack entry. (Always the last entry.) */ + _PyErr_StackItem _exc_state; + + /* The bottom-most frame on the stack. */ + CFrame _root_cframe; }; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 20508d4a687..53d0b5c4549 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -12,9 +12,14 @@ extern "C" { struct pyruntimestate; struct _ceval_runtime_state; +#ifndef Py_DEFAULT_RECURSION_LIMIT +# define Py_DEFAULT_RECURSION_LIMIT 1000 +#endif + #include "pycore_interp.h" // PyInterpreterState.eval_frame #include "pycore_pystate.h" // _PyThreadState_GET() + extern void _Py_FinishPendingCalls(PyThreadState *tstate); extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *); extern void _PyEval_InitState(struct _ceval_state *, PyThread_type_lock); diff --git a/Include/internal/pycore_runtime_init.h b/Include/internal/pycore_runtime_init.h index fc768ff5d05..aa6612063d0 100644 --- a/Include/internal/pycore_runtime_init.h +++ b/Include/internal/pycore_runtime_init.h @@ -41,6 +41,8 @@ extern "C" { #define _PyThreadState_INIT \ { \ ._static = 1, \ + .recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \ + .context_ver = 1, \ } diff --git a/Python/ceval.c b/Python/ceval.c index d33cd4e1edb..eed902fc687 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -737,10 +737,6 @@ Py_MakePendingCalls(void) /* The interpreter's recursion limit */ -#ifndef Py_DEFAULT_RECURSION_LIMIT -# define Py_DEFAULT_RECURSION_LIMIT 1000 -#endif - void _PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval) { diff --git a/Python/pystate.c b/Python/pystate.c index 50b36218960..23156850bfe 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -775,21 +775,19 @@ init_threadstate(PyThreadState *tstate, next->prev = tstate; } tstate->next = next; - tstate->prev = NULL; + assert(tstate->prev == NULL); tstate->thread_id = PyThread_get_thread_ident(); #ifdef PY_HAVE_THREAD_NATIVE_ID tstate->native_thread_id = PyThread_get_thread_native_id(); #endif - tstate->context_ver = 1; - tstate->recursion_limit = interp->ceval.recursion_limit, tstate->recursion_remaining = interp->ceval.recursion_limit, - tstate->exc_info = &tstate->exc_state; + tstate->exc_info = &tstate->_exc_state; - tstate->cframe = &tstate->root_cframe; + tstate->cframe = &tstate->_root_cframe; tstate->datastack_chunk = NULL; tstate->datastack_top = NULL; tstate->datastack_limit = NULL; @@ -1027,10 +1025,10 @@ PyThreadState_Clear(PyThreadState *tstate) Py_CLEAR(tstate->curexc_value); Py_CLEAR(tstate->curexc_traceback); - Py_CLEAR(tstate->exc_state.exc_value); + Py_CLEAR(tstate->_exc_state.exc_value); /* The stack of exception states should contain just this thread. */ - if (verbose && tstate->exc_info != &tstate->exc_state) { + if (verbose && tstate->exc_info != &tstate->_exc_state) { fprintf(stderr, "PyThreadState_Clear: warning: thread still has a generator\n"); }