From f32fe6df93904baf317ba259e1d9676f807a2142 Mon Sep 17 00:00:00 2001 From: Neil Schemenauer Date: Tue, 13 Oct 2026 11:49:00 -0700 Subject: [PATCH] [3.15] Fix _PyThreadState_ResumeDetached() assertion with attach waiters Make _PyThreadState_ResumeDetached() waiter-aware and use it from start_the_world(), matching main branch. Remove tstate_resume(). --- Include/internal/pycore_pystate.h | 5 ++-- Python/pystate.c | 46 ++++++++++++------------------- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/Include/internal/pycore_pystate.h b/Include/internal/pycore_pystate.h index 78dc64b645e8c0d..16edeccb7224beb 100644 --- a/Include/internal/pycore_pystate.h +++ b/Include/internal/pycore_pystate.h @@ -161,8 +161,9 @@ extern void _PyThreadState_Suspend(PyThreadState *tstate); // Returns 1 on success, 0 if the thread was not in the "detached" state. extern int _PyThreadState_TrySuspendDetached(PyThreadState *tstate); -// Undo a successful _PyThreadState_TrySuspendDetached(): switch the thread -// back to "detached" and wake it if it is waiting to attach. +// Resume a thread suspended by _PyThreadState_TrySuspendDetached() or a +// stop-the-world pause: switch it back to "detached" or "detached-waiting" +// and wake it if it is waiting to attach. extern void _PyThreadState_ResumeDetached(PyThreadState *tstate); #endif diff --git a/Python/pystate.c b/Python/pystate.c index 19cf1e8dfa6b14d..3a748597d9cd177 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -2401,10 +2401,24 @@ void _PyThreadState_ResumeDetached(PyThreadState *tstate) { assert(tstate != _PyThreadState_GET()); - assert(_Py_atomic_load_int_relaxed(&tstate->state) == _Py_THREAD_SUSPENDED); - _Py_atomic_store_int(&tstate->state, _Py_THREAD_DETACHED); + int state = _Py_atomic_load_int_relaxed(&tstate->state); + int next_state; + do { + assert(state == _Py_THREAD_SUSPENDED || + state == _Py_THREAD_SUSPENDED_WAITING); + if (state == _Py_THREAD_SUSPENDED_WAITING) { + next_state = _Py_THREAD_DETACHED_WAITING; + } + else { + next_state = _Py_THREAD_DETACHED; + } + // Retry if an attach waiter registered concurrently. + } while (!_Py_atomic_compare_exchange_int( + &tstate->state, &state, next_state)); // Wake the thread if it is parked in tstate_wait_attach(). - _PyParkingLot_UnparkAll(&tstate->state); + if (state == _Py_THREAD_SUSPENDED_WAITING) { + _PyParkingLot_UnparkAll(&tstate->state); + } } #endif @@ -2526,30 +2540,6 @@ stop_the_world(struct _stoptheworld_state *stw) stw->world_stopped = 1; } -static void -tstate_resume(PyThreadState *tstate) -{ - assert(tstate != _PyThreadState_GET()); - int state = _Py_atomic_load_int_relaxed(&tstate->state); - int next_state; - do { - assert(state == _Py_THREAD_SUSPENDED || - state == _Py_THREAD_SUSPENDED_WAITING); - if (state == _Py_THREAD_SUSPENDED_WAITING) { - next_state = _Py_THREAD_DETACHED_WAITING; - } - else { - next_state = _Py_THREAD_DETACHED; - } - // Retry if an attach waiter registered concurrently. - } while (!_Py_atomic_compare_exchange_int( - &tstate->state, &state, next_state)); - // Wake the thread if it is parked in tstate_wait_attach(). - if (state == _Py_THREAD_SUSPENDED_WAITING) { - _PyParkingLot_UnparkAll(&tstate->state); - } -} - static void start_the_world(struct _stoptheworld_state *stw) { @@ -2563,7 +2553,7 @@ start_the_world(struct _stoptheworld_state *stw) _Py_FOR_EACH_STW_INTERP(stw, i) { _Py_FOR_EACH_TSTATE_UNLOCKED(i, t) { if (t != stw->requester) { - tstate_resume(t); + _PyThreadState_ResumeDetached(t); } } }