From 324908ba936d5d262026deebb81f050803848c41 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Thu, 13 Jan 2022 17:09:24 -0700 Subject: bpo-45953: Statically initialize all the PyThreadState fields we can. (gh-30590) https://bugs.python.org/issue45953 --- Include/cpython/pystate.h | 42 +++++++++++++++++++++++----------- Include/internal/pycore_ceval.h | 5 ++++ Include/internal/pycore_runtime_init.h | 2 ++ Python/ceval.c | 4 ---- Python/pystate.c | 12 ++++------ 5 files changed, 41 insertions(+), 24 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index bcb1bb2..a35e5b8 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 20508d4..53d0b5c 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 fc768ff..aa66120 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 d33cd4e..eed902f 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 50b3621..2315685 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"); } -- cgit v0.12