Skip to content

bpo-45953: Statically initialize all the PyThreadState fields we can. #30590

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 29 additions & 13 deletions Include/cpython/pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
};


Expand Down
5 changes: 5 additions & 0 deletions Include/internal/pycore_ceval.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_runtime_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ extern "C" {
#define _PyThreadState_INIT \
{ \
._static = 1, \
.recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \
.context_ver = 1, \
}


Expand Down
4 changes: 0 additions & 4 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
12 changes: 5 additions & 7 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -785,21 +785,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;
Expand Down Expand Up @@ -1037,10 +1035,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");
}
Expand Down