@@ -1939,8 +1939,9 @@ tstate_delete_common(PyThreadState *tstate, int release_gil)
19391939 if (tstate -> next ) {
19401940 tstate -> next -> prev = tstate -> prev ;
19411941 }
1942- if (tstate -> state != _Py_THREAD_SUSPENDED &&
1943- tstate -> state != _Py_THREAD_SUSPENDED_DETACHED )
1942+ int state = _Py_atomic_load_int_relaxed (& tstate -> state );
1943+ if (state != _Py_THREAD_SUSPENDED &&
1944+ state != _Py_THREAD_SUSPENDED_WAITING )
19441945 {
19451946 // Any ongoing stop-the-world request should not wait for us because
19461947 // our thread is getting deleted.
@@ -2239,23 +2240,20 @@ tstate_set_detached(PyThreadState *tstate, int detached_state)
22392240static void
22402241tstate_wait_attach (PyThreadState * tstate )
22412242{
2242- #ifdef Py_GIL_DISABLED
2243- _PyThreadStateImpl * tstate_impl = (_PyThreadStateImpl * )tstate ;
2244- int stw_attach_waiting = 0 ;
2245- #endif
2246- do {
2243+ for (;;) {
22472244 int state = _Py_atomic_load_int_relaxed (& tstate -> state );
2248- if (state == _Py_THREAD_SUSPENDED ||
2249- state == _Py_THREAD_SUSPENDED_DETACHED )
2250- {
2251- #ifdef Py_GIL_DISABLED
2252- if (state == _Py_THREAD_SUSPENDED_DETACHED ) {
2253- stw_attach_waiting = 1 ;
2254- _Py_atomic_store_int_relaxed (
2255- & tstate_impl -> stw_attach_waiting , 1 );
2245+ if (state == _Py_THREAD_SUSPENDED ) {
2246+ // Register an active attach waiter. The next stop-the-world
2247+ // request must let this thread attach before suspending it again.
2248+ if (!_Py_atomic_compare_exchange_int (
2249+ & tstate -> state , & state , _Py_THREAD_SUSPENDED_WAITING ))
2250+ {
2251+ continue ;
22562252 }
2257- #endif
2258- // Wait until we're switched out of SUSPENDED to DETACHED.
2253+ state = _Py_THREAD_SUSPENDED_WAITING ;
2254+ }
2255+ if (state == _Py_THREAD_SUSPENDED_WAITING ) {
2256+ // Wait until the stop-the-world thread lets us attach.
22592257 _PyParkingLot_Park (& tstate -> state , & state , sizeof (tstate -> state ),
22602258 /*timeout=*/ -1 , NULL , /*detach=*/ 0 );
22612259 }
@@ -2264,15 +2262,15 @@ tstate_wait_attach(PyThreadState *tstate)
22642262 _PyThreadState_HangThread (tstate );
22652263 }
22662264 else {
2267- assert (state == _Py_THREAD_DETACHED );
2265+ assert (state == _Py_THREAD_DETACHED ||
2266+ state == _Py_THREAD_DETACHED_WAITING );
2267+ if (_Py_atomic_compare_exchange_int (
2268+ & tstate -> state , & state , _Py_THREAD_ATTACHED ))
2269+ {
2270+ return ;
2271+ }
22682272 }
2269- // Once we're back in DETACHED we can re-attach
2270- } while (!tstate_try_attach (tstate ));
2271- #ifdef Py_GIL_DISABLED
2272- if (stw_attach_waiting ) {
2273- _Py_atomic_store_int_relaxed (& tstate_impl -> stw_attach_waiting , 0 );
22742273 }
2275- #endif
22762274}
22772275
22782276void
@@ -2441,17 +2439,12 @@ park_detached_threads(struct _stoptheworld_state *stw)
24412439 _Py_FOR_EACH_STW_INTERP (stw , i ) {
24422440 _Py_FOR_EACH_TSTATE_UNLOCKED (i , t ) {
24432441 int state = _Py_atomic_load_int_relaxed (& t -> state );
2442+ // DETACHED_WAITING threads remain counted until they attach and
2443+ // stop, so repeated pauses cannot prevent them from attaching.
24442444 if (state == _Py_THREAD_DETACHED ) {
2445- _PyThreadStateImpl * tstate_impl = (_PyThreadStateImpl * )t ;
2446- if (_Py_atomic_load_int_relaxed (
2447- & tstate_impl -> stw_attach_waiting ))
2448- {
2449- continue ;
2450- }
24512445 // Atomically transition to "suspended" if in "detached" state.
24522446 if (_Py_atomic_compare_exchange_int (
2453- & t -> state , & state ,
2454- _Py_THREAD_SUSPENDED_DETACHED )) {
2447+ & t -> state , & state , _Py_THREAD_SUSPENDED )) {
24552448 num_parked ++ ;
24562449 }
24572450 }
@@ -2536,12 +2529,17 @@ start_the_world(struct _stoptheworld_state *stw)
25362529 _Py_FOR_EACH_STW_INTERP (stw , i ) {
25372530 _Py_FOR_EACH_TSTATE_UNLOCKED (i , t ) {
25382531 if (t != stw -> requester ) {
2539- #ifndef NDEBUG
25402532 int state = _Py_atomic_load_int_relaxed (& t -> state );
2541- assert (state == _Py_THREAD_SUSPENDED ||
2542- state == _Py_THREAD_SUSPENDED_DETACHED );
2543- #endif
2544- _Py_atomic_store_int (& t -> state , _Py_THREAD_DETACHED );
2533+ int next_state ;
2534+ do {
2535+ assert (state == _Py_THREAD_SUSPENDED ||
2536+ state == _Py_THREAD_SUSPENDED_WAITING );
2537+ next_state = (state == _Py_THREAD_SUSPENDED_WAITING
2538+ ? _Py_THREAD_DETACHED_WAITING
2539+ : _Py_THREAD_DETACHED );
2540+ // Retry if an attach waiter registered concurrently.
2541+ } while (!_Py_atomic_compare_exchange_int (
2542+ & t -> state , & state , next_state ));
25452543 _PyParkingLot_UnparkAll (& t -> state );
25462544 }
25472545 }
0 commit comments