Skip to content

Commit 6213ab4

Browse files
gh-152741: Publish thread states to the interpreter's thread list only after they are fully initialized
1 parent 999a046 commit 6213ab4

1 file changed

Lines changed: 39 additions & 24 deletions

File tree

Python/pystate.c

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ static void bind_gilstate_tstate(PyThreadState *);
159159
static void unbind_gilstate_tstate(PyThreadState *);
160160

161161
static void tstate_mimalloc_bind(PyThreadState *);
162+
static void add_threadstate(PyInterpreterState *interp, PyThreadState *tstate,
163+
PyThreadState *next);
162164

163165
static void
164166
bind_tstate(PyThreadState *tstate)
@@ -190,6 +192,18 @@ bind_tstate(PyThreadState *tstate)
190192
tstate_mimalloc_bind(tstate);
191193

192194
tstate->_status.bound = 1;
195+
196+
// Publish the thread state to the interpreter's thread list only now
197+
// that it is fully initialized, so that readers of the list never see
198+
// a partially initialized thread state.
199+
PyInterpreterState *interp = tstate->interp;
200+
HEAD_LOCK(interp->runtime);
201+
if (interp->stoptheworld.requested || _PyRuntime.stoptheworld.requested) {
202+
// Start in the suspended state if there is an ongoing stop-the-world.
203+
tstate->state = _Py_THREAD_SUSPENDED;
204+
}
205+
add_threadstate(interp, tstate, interp->threads.head);
206+
HEAD_UNLOCK(interp->runtime);
193207
}
194208

195209
static void
@@ -1638,10 +1652,6 @@ init_threadstate(_PyThreadStateImpl *_tstate,
16381652

16391653
llist_init(&_tstate->mem_free_queue);
16401654
llist_init(&_tstate->asyncio_tasks_head);
1641-
if (interp->stoptheworld.requested || _PyRuntime.stoptheworld.requested) {
1642-
// Start in the suspended state if there is an ongoing stop-the-world.
1643-
tstate->state = _Py_THREAD_SUSPENDED;
1644-
}
16451655

16461656
tstate->_status.initialized = 1;
16471657
}
@@ -1699,10 +1709,6 @@ new_threadstate(PyInterpreterState *interp, int whence)
16991709
uint64_t id = interp->threads.next_unique_id;
17001710
init_threadstate(tstate, interp, id, whence);
17011711

1702-
// Add the new thread state to the interpreter.
1703-
PyThreadState *old_head = interp->threads.head;
1704-
add_threadstate(interp, (PyThreadState *)tstate, old_head);
1705-
17061712
HEAD_UNLOCK(interp->runtime);
17071713

17081714
#ifdef Py_GIL_DISABLED
@@ -1930,24 +1936,33 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
19301936
_PyRuntimeState *runtime = interp->runtime;
19311937

19321938
HEAD_LOCK(runtime);
1933-
if (tstate->prev) {
1934-
tstate->prev->next = tstate->next;
1935-
}
1936-
else {
1937-
interp->threads.head = tstate->next;
1938-
}
1939-
if (tstate->next) {
1940-
tstate->next->prev = tstate->prev;
1941-
}
1942-
if (tstate->state != _Py_THREAD_SUSPENDED) {
1943-
// Any ongoing stop-the-world request should not wait for us because
1944-
// our thread is getting deleted.
1945-
if (interp->stoptheworld.requested) {
1946-
decrement_stoptheworld_countdown(&interp->stoptheworld);
1939+
if (tstate->_status.bound) {
1940+
if (tstate->prev) {
1941+
tstate->prev->next = tstate->next;
19471942
}
1948-
if (runtime->stoptheworld.requested) {
1949-
decrement_stoptheworld_countdown(&runtime->stoptheworld);
1943+
else {
1944+
interp->threads.head = tstate->next;
1945+
}
1946+
if (tstate->next) {
1947+
tstate->next->prev = tstate->prev;
19501948
}
1949+
if (tstate->state != _Py_THREAD_SUSPENDED) {
1950+
// Any ongoing stop-the-world request should not wait for us
1951+
// because our thread is getting deleted.
1952+
if (interp->stoptheworld.requested) {
1953+
decrement_stoptheworld_countdown(&interp->stoptheworld);
1954+
}
1955+
if (runtime->stoptheworld.requested) {
1956+
decrement_stoptheworld_countdown(&runtime->stoptheworld);
1957+
}
1958+
}
1959+
}
1960+
else {
1961+
// The thread state was never bound, so it was never added to the
1962+
// interpreter's thread list and no stop-the-world request is
1963+
// waiting on it.
1964+
assert(tstate->prev == NULL && tstate->next == NULL);
1965+
assert(interp->threads.head != tstate);
19511966
}
19521967

19531968
#if defined(Py_REF_DEBUG) && defined(Py_GIL_DISABLED)

0 commit comments

Comments
 (0)