Skip to content

Commit 77195cd

Browse files
authored
bpo-46090: Allow PyThreadState.datastack_* members to be NULL (GH-30234)
1 parent 02b5417 commit 77195cd

File tree

2 files changed

+24
-25
lines changed

2 files changed

+24
-25
lines changed

Include/internal/pycore_frame.h

+7-4
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,13 @@ static inline InterpreterFrame *
174174
_PyThreadState_BumpFramePointer(PyThreadState *tstate, size_t size)
175175
{
176176
PyObject **base = tstate->datastack_top;
177-
PyObject **top = base + size;
178-
if (top < tstate->datastack_limit) {
179-
tstate->datastack_top = top;
180-
return (InterpreterFrame *)base;
177+
if (base) {
178+
PyObject **top = base + size;
179+
assert(tstate->datastack_limit);
180+
if (top < tstate->datastack_limit) {
181+
tstate->datastack_top = top;
182+
return (InterpreterFrame *)base;
183+
}
181184
}
182185
return _PyThreadState_BumpFramePointerSlow(tstate, size);
183186
}

Python/pystate.c

+17-21
Original file line numberDiff line numberDiff line change
@@ -749,8 +749,7 @@ free_threadstate(PyThreadState *tstate)
749749
static void
750750
init_threadstate(PyThreadState *tstate,
751751
PyInterpreterState *interp, uint64_t id,
752-
PyThreadState *next,
753-
_PyStackChunk *datastack_chunk)
752+
PyThreadState *next)
754753
{
755754
if (tstate->_initialized) {
756755
Py_FatalError("thread state already initialized");
@@ -784,11 +783,9 @@ init_threadstate(PyThreadState *tstate,
784783
tstate->exc_info = &tstate->exc_state;
785784

786785
tstate->cframe = &tstate->root_cframe;
787-
assert(datastack_chunk != NULL);
788-
tstate->datastack_chunk = datastack_chunk;
789-
/* If top points to entry 0, then _PyThreadState_PopFrame will try to pop this chunk */
790-
tstate->datastack_top = &tstate->datastack_chunk->data[1];
791-
tstate->datastack_limit = (PyObject **)(((char *)tstate->datastack_chunk) + DATA_STACK_CHUNK_SIZE);
786+
tstate->datastack_chunk = NULL;
787+
tstate->datastack_top = NULL;
788+
tstate->datastack_limit = NULL;
792789

793790
tstate->_initialized = 1;
794791
}
@@ -799,11 +796,6 @@ new_threadstate(PyInterpreterState *interp)
799796
PyThreadState *tstate;
800797
_PyRuntimeState *runtime = interp->runtime;
801798

802-
_PyStackChunk *datastack_chunk = allocate_chunk(DATA_STACK_CHUNK_SIZE, NULL);
803-
if (datastack_chunk == NULL) {
804-
return NULL;
805-
}
806-
807799
/* We serialize concurrent creation to protect global state. */
808800
HEAD_LOCK(runtime);
809801

@@ -833,14 +825,13 @@ new_threadstate(PyInterpreterState *interp)
833825
}
834826
interp->threads.head = tstate;
835827

836-
init_threadstate(tstate, interp, id, old_head, datastack_chunk);
828+
init_threadstate(tstate, interp, id, old_head);
837829

838830
HEAD_UNLOCK(runtime);
839831
return tstate;
840832

841833
error:
842834
HEAD_UNLOCK(runtime);
843-
_PyObject_VirtualFree(datastack_chunk, datastack_chunk->size);
844835
return NULL;
845836
}
846837

@@ -2186,8 +2177,6 @@ _Py_GetConfig(void)
21862177
static PyObject **
21872178
push_chunk(PyThreadState *tstate, int size)
21882179
{
2189-
assert(tstate->datastack_top + size >= tstate->datastack_limit);
2190-
21912180
int allocate_size = DATA_STACK_CHUNK_SIZE;
21922181
while (allocate_size < (int)sizeof(PyObject*)*(size + MINIMUM_OVERHEAD)) {
21932182
allocate_size *= 2;
@@ -2196,10 +2185,16 @@ push_chunk(PyThreadState *tstate, int size)
21962185
if (new == NULL) {
21972186
return NULL;
21982187
}
2199-
tstate->datastack_chunk->top = tstate->datastack_top - &tstate->datastack_chunk->data[0];
2188+
if (tstate->datastack_chunk) {
2189+
tstate->datastack_chunk->top = tstate->datastack_top -
2190+
&tstate->datastack_chunk->data[0];
2191+
}
22002192
tstate->datastack_chunk = new;
22012193
tstate->datastack_limit = (PyObject **)(((char *)new) + allocate_size);
2202-
PyObject **res = &new->data[0];
2194+
// When new is the "root" chunk (i.e. new->previous == NULL), we can keep
2195+
// _PyThreadState_PopFrame from freeing it later by "skipping" over the
2196+
// first element:
2197+
PyObject **res = &new->data[new->previous == NULL];
22032198
tstate->datastack_top = res + size;
22042199
return res;
22052200
}
@@ -2212,9 +2207,6 @@ _PyThreadState_BumpFramePointerSlow(PyThreadState *tstate, size_t size)
22122207
PyObject **top = base + size;
22132208
if (top >= tstate->datastack_limit) {
22142209
base = push_chunk(tstate, (int)size);
2215-
if (base == NULL) {
2216-
return NULL;
2217-
}
22182210
}
22192211
else {
22202212
tstate->datastack_top = top;
@@ -2244,16 +2236,20 @@ _PyThreadState_PushFrame(PyThreadState *tstate, PyFunctionObject *func, PyObject
22442236
void
22452237
_PyThreadState_PopFrame(PyThreadState *tstate, InterpreterFrame * frame)
22462238
{
2239+
assert(tstate->datastack_chunk);
22472240
PyObject **base = (PyObject **)frame;
22482241
if (base == &tstate->datastack_chunk->data[0]) {
22492242
_PyStackChunk *chunk = tstate->datastack_chunk;
22502243
_PyStackChunk *previous = chunk->previous;
2244+
// push_chunk ensures that the root chunk is never popped:
2245+
assert(previous);
22512246
tstate->datastack_top = &previous->data[previous->top];
22522247
tstate->datastack_chunk = previous;
22532248
_PyObject_VirtualFree(chunk, chunk->size);
22542249
tstate->datastack_limit = (PyObject **)(((char *)previous) + previous->size);
22552250
}
22562251
else {
2252+
assert(tstate->datastack_top);
22572253
assert(tstate->datastack_top >= base);
22582254
tstate->datastack_top = base;
22592255
}

0 commit comments

Comments
 (0)