Skip to content

Commit 324908b

Browse files
bpo-45953: Statically initialize all the PyThreadState fields we can. (gh-30590)
https://bugs.python.org/issue45953
1 parent d4e64cd commit 324908b

File tree

5 files changed

+41
-24
lines changed

5 files changed

+41
-24
lines changed

Include/cpython/pystate.h

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,19 @@ typedef struct _cframe {
5353
} CFrame;
5454

5555
typedef struct _err_stackitem {
56-
/* This struct represents an entry on the exception stack, which is a
57-
* per-coroutine state. (Coroutine in the computer science sense,
58-
* including the thread and generators).
59-
* This ensures that the exception state is not impacted by "yields"
60-
* from an except handler.
56+
/* This struct represents a single execution context where we might
57+
* be currently handling an exception. It is a per-coroutine state
58+
* (coroutine in the computer science sense, including the thread
59+
* and generators).
60+
*
61+
* This is used as an entry on the exception stack, where each
62+
* entry indicates if it is currently handling an exception.
63+
* This ensures that the exception state is not impacted
64+
* by "yields" from an except handler. The thread
65+
* always has an entry (the bottom-most one).
6166
*/
67+
68+
/* The exception currently being handled in this context, if any. */
6269
PyObject *exc_value;
6370

6471
struct _err_stackitem *previous_item;
@@ -112,13 +119,9 @@ struct _ts {
112119
PyObject *curexc_value;
113120
PyObject *curexc_traceback;
114121

115-
/* The exception currently being handled, if no coroutines/generators
116-
* are present. Always last element on the stack referred to be exc_info.
117-
*/
118-
_PyErr_StackItem exc_state;
119-
120-
/* Pointer to the top of the stack of the exceptions currently
121-
* being handled */
122+
/* Pointer to the top of the exception stack for the exceptions
123+
* we may be currently handling. (See _PyErr_StackItem above.)
124+
* This is never NULL. */
122125
_PyErr_StackItem *exc_info;
123126

124127
PyObject *dict; /* Stores per-thread state */
@@ -174,13 +177,26 @@ struct _ts {
174177
/* Unique thread state id. */
175178
uint64_t id;
176179

177-
CFrame root_cframe;
178180
PyTraceInfo trace_info;
179181

180182
_PyStackChunk *datastack_chunk;
181183
PyObject **datastack_top;
182184
PyObject **datastack_limit;
183185
/* XXX signal handlers should also be here */
186+
187+
/* The following fields are here to avoid allocation during init.
188+
The data is exposed through PyThreadState pointer fields.
189+
These fields should not be accessed directly outside of init.
190+
191+
All other PyInterpreterState pointer fields are populated when
192+
needed and default to NULL.
193+
*/
194+
195+
/* The thread's exception stack entry. (Always the last entry.) */
196+
_PyErr_StackItem _exc_state;
197+
198+
/* The bottom-most frame on the stack. */
199+
CFrame _root_cframe;
184200
};
185201

186202

Include/internal/pycore_ceval.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@ extern "C" {
1212
struct pyruntimestate;
1313
struct _ceval_runtime_state;
1414

15+
#ifndef Py_DEFAULT_RECURSION_LIMIT
16+
# define Py_DEFAULT_RECURSION_LIMIT 1000
17+
#endif
18+
1519
#include "pycore_interp.h" // PyInterpreterState.eval_frame
1620
#include "pycore_pystate.h" // _PyThreadState_GET()
1721

22+
1823
extern void _Py_FinishPendingCalls(PyThreadState *tstate);
1924
extern void _PyEval_InitRuntimeState(struct _ceval_runtime_state *);
2025
extern void _PyEval_InitState(struct _ceval_state *, PyThread_type_lock);

Include/internal/pycore_runtime_init.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ extern "C" {
4141
#define _PyThreadState_INIT \
4242
{ \
4343
._static = 1, \
44+
.recursion_limit = Py_DEFAULT_RECURSION_LIMIT, \
45+
.context_ver = 1, \
4446
}
4547

4648

Python/ceval.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -737,10 +737,6 @@ Py_MakePendingCalls(void)
737737

738738
/* The interpreter's recursion limit */
739739

740-
#ifndef Py_DEFAULT_RECURSION_LIMIT
741-
# define Py_DEFAULT_RECURSION_LIMIT 1000
742-
#endif
743-
744740
void
745741
_PyEval_InitRuntimeState(struct _ceval_runtime_state *ceval)
746742
{

Python/pystate.c

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -775,21 +775,19 @@ init_threadstate(PyThreadState *tstate,
775775
next->prev = tstate;
776776
}
777777
tstate->next = next;
778-
tstate->prev = NULL;
778+
assert(tstate->prev == NULL);
779779

780780
tstate->thread_id = PyThread_get_thread_ident();
781781
#ifdef PY_HAVE_THREAD_NATIVE_ID
782782
tstate->native_thread_id = PyThread_get_thread_native_id();
783783
#endif
784784

785-
tstate->context_ver = 1;
786-
787785
tstate->recursion_limit = interp->ceval.recursion_limit,
788786
tstate->recursion_remaining = interp->ceval.recursion_limit,
789787

790-
tstate->exc_info = &tstate->exc_state;
788+
tstate->exc_info = &tstate->_exc_state;
791789

792-
tstate->cframe = &tstate->root_cframe;
790+
tstate->cframe = &tstate->_root_cframe;
793791
tstate->datastack_chunk = NULL;
794792
tstate->datastack_top = NULL;
795793
tstate->datastack_limit = NULL;
@@ -1027,10 +1025,10 @@ PyThreadState_Clear(PyThreadState *tstate)
10271025
Py_CLEAR(tstate->curexc_value);
10281026
Py_CLEAR(tstate->curexc_traceback);
10291027

1030-
Py_CLEAR(tstate->exc_state.exc_value);
1028+
Py_CLEAR(tstate->_exc_state.exc_value);
10311029

10321030
/* The stack of exception states should contain just this thread. */
1033-
if (verbose && tstate->exc_info != &tstate->exc_state) {
1031+
if (verbose && tstate->exc_info != &tstate->_exc_state) {
10341032
fprintf(stderr,
10351033
"PyThreadState_Clear: warning: thread still has a generator\n");
10361034
}

0 commit comments

Comments
 (0)