@@ -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
@@ -2462,17 +2460,12 @@ park_detached_threads(struct _stoptheworld_state *stw)
24622460 _Py_FOR_EACH_STW_INTERP (stw , i ) {
24632461 _Py_FOR_EACH_TSTATE_UNLOCKED (i , t ) {
24642462 int state = _Py_atomic_load_int_relaxed (& t -> state );
2463+ // DETACHED_WAITING threads remain counted until they attach and
2464+ // stop, so repeated pauses cannot prevent them from attaching.
24652465 if (state == _Py_THREAD_DETACHED ) {
2466- _PyThreadStateImpl * tstate_impl = (_PyThreadStateImpl * )t ;
2467- if (_Py_atomic_load_int_relaxed (
2468- & tstate_impl -> stw_attach_waiting ))
2469- {
2470- continue ;
2471- }
24722466 // Atomically transition to "suspended" if in "detached" state.
24732467 if (_Py_atomic_compare_exchange_int (
2474- & t -> state , & state ,
2475- _Py_THREAD_SUSPENDED_DETACHED )) {
2468+ & t -> state , & state , _Py_THREAD_SUSPENDED )) {
24762469 num_parked ++ ;
24772470 }
24782471 }
@@ -2557,12 +2550,17 @@ start_the_world(struct _stoptheworld_state *stw)
25572550 _Py_FOR_EACH_STW_INTERP (stw , i ) {
25582551 _Py_FOR_EACH_TSTATE_UNLOCKED (i , t ) {
25592552 if (t != stw -> requester ) {
2560- #ifndef NDEBUG
25612553 int state = _Py_atomic_load_int_relaxed (& t -> state );
2562- assert (state == _Py_THREAD_SUSPENDED ||
2563- state == _Py_THREAD_SUSPENDED_DETACHED );
2564- #endif
2565- _Py_atomic_store_int (& t -> state , _Py_THREAD_DETACHED );
2554+ int next_state ;
2555+ do {
2556+ assert (state == _Py_THREAD_SUSPENDED ||
2557+ state == _Py_THREAD_SUSPENDED_WAITING );
2558+ next_state = (state == _Py_THREAD_SUSPENDED_WAITING
2559+ ? _Py_THREAD_DETACHED_WAITING
2560+ : _Py_THREAD_DETACHED );
2561+ // Retry if an attach waiter registered concurrently.
2562+ } while (!_Py_atomic_compare_exchange_int (
2563+ & t -> state , & state , next_state ));
25662564 _PyParkingLot_UnparkAll (& t -> state );
25672565 }
25682566 }
0 commit comments