https://github.com/python/cpython/commit/ef498e03366065319d57c1ce65e1d7bdcefbb498
commit: ef498e03366065319d57c1ce65e1d7bdcefbb498
branch: 3.15
author: Neil Schemenauer <[email protected]>
committer: nascheme <[email protected]>
date: 2026-10-13T12:43:19-07:00
summary:
[3.15] Fix _PyThreadState_ResumeDetached() assertion with attach waiters
(GH-159372)
Make _PyThreadState_ResumeDetached() waiter-aware and use it from
start_the_world(), matching main branch. Remove tstate_resume().
files:
M Include/internal/pycore_pystate.h
M Python/pystate.c
diff --git a/Include/internal/pycore_pystate.h
b/Include/internal/pycore_pystate.h
index 78dc64b645e8c0..16edeccb7224be 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 19cf1e8dfa6b14..3a748597d9cd17 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);
}
}
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]