Skip to content

Commit ef498e0

Browse files
authored
[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().
1 parent ed4f3bc commit ef498e0

2 files changed

Lines changed: 21 additions & 30 deletions

File tree

‎Include/internal/pycore_pystate.h‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,9 @@ extern void _PyThreadState_Suspend(PyThreadState *tstate);
161161
// Returns 1 on success, 0 if the thread was not in the "detached" state.
162162
extern int _PyThreadState_TrySuspendDetached(PyThreadState *tstate);
163163

164-
// Undo a successful _PyThreadState_TrySuspendDetached(): switch the thread
165-
// back to "detached" and wake it if it is waiting to attach.
164+
// Resume a thread suspended by _PyThreadState_TrySuspendDetached() or a
165+
// stop-the-world pause: switch it back to "detached" or "detached-waiting"
166+
// and wake it if it is waiting to attach.
166167
extern void _PyThreadState_ResumeDetached(PyThreadState *tstate);
167168
#endif
168169

‎Python/pystate.c‎

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2401,10 +2401,24 @@ void
24012401
_PyThreadState_ResumeDetached(PyThreadState *tstate)
24022402
{
24032403
assert(tstate != _PyThreadState_GET());
2404-
assert(_Py_atomic_load_int_relaxed(&tstate->state) == _Py_THREAD_SUSPENDED);
2405-
_Py_atomic_store_int(&tstate->state, _Py_THREAD_DETACHED);
2404+
int state = _Py_atomic_load_int_relaxed(&tstate->state);
2405+
int next_state;
2406+
do {
2407+
assert(state == _Py_THREAD_SUSPENDED ||
2408+
state == _Py_THREAD_SUSPENDED_WAITING);
2409+
if (state == _Py_THREAD_SUSPENDED_WAITING) {
2410+
next_state = _Py_THREAD_DETACHED_WAITING;
2411+
}
2412+
else {
2413+
next_state = _Py_THREAD_DETACHED;
2414+
}
2415+
// Retry if an attach waiter registered concurrently.
2416+
} while (!_Py_atomic_compare_exchange_int(
2417+
&tstate->state, &state, next_state));
24062418
// Wake the thread if it is parked in tstate_wait_attach().
2407-
_PyParkingLot_UnparkAll(&tstate->state);
2419+
if (state == _Py_THREAD_SUSPENDED_WAITING) {
2420+
_PyParkingLot_UnparkAll(&tstate->state);
2421+
}
24082422
}
24092423
#endif
24102424

@@ -2526,30 +2540,6 @@ stop_the_world(struct _stoptheworld_state *stw)
25262540
stw->world_stopped = 1;
25272541
}
25282542

2529-
static void
2530-
tstate_resume(PyThreadState *tstate)
2531-
{
2532-
assert(tstate != _PyThreadState_GET());
2533-
int state = _Py_atomic_load_int_relaxed(&tstate->state);
2534-
int next_state;
2535-
do {
2536-
assert(state == _Py_THREAD_SUSPENDED ||
2537-
state == _Py_THREAD_SUSPENDED_WAITING);
2538-
if (state == _Py_THREAD_SUSPENDED_WAITING) {
2539-
next_state = _Py_THREAD_DETACHED_WAITING;
2540-
}
2541-
else {
2542-
next_state = _Py_THREAD_DETACHED;
2543-
}
2544-
// Retry if an attach waiter registered concurrently.
2545-
} while (!_Py_atomic_compare_exchange_int(
2546-
&tstate->state, &state, next_state));
2547-
// Wake the thread if it is parked in tstate_wait_attach().
2548-
if (state == _Py_THREAD_SUSPENDED_WAITING) {
2549-
_PyParkingLot_UnparkAll(&tstate->state);
2550-
}
2551-
}
2552-
25532543
static void
25542544
start_the_world(struct _stoptheworld_state *stw)
25552545
{
@@ -2563,7 +2553,7 @@ start_the_world(struct _stoptheworld_state *stw)
25632553
_Py_FOR_EACH_STW_INTERP(stw, i) {
25642554
_Py_FOR_EACH_TSTATE_UNLOCKED(i, t) {
25652555
if (t != stw->requester) {
2566-
tstate_resume(t);
2556+
_PyThreadState_ResumeDetached(t);
25672557
}
25682558
}
25692559
}

0 commit comments

Comments
 (0)