@@ -1461,6 +1461,15 @@ free_threadstate(_PyThreadStateImpl *tstate)
14611461 }
14621462}
14631463
1464+ static void
1465+ decref_threadstate (_PyThreadStateImpl * tstate )
1466+ {
1467+ if (_Py_atomic_add_ssize (& tstate -> refcount , -1 ) == 1 ) {
1468+ // The last reference to the thread state is gone.
1469+ free_threadstate (tstate );
1470+ }
1471+ }
1472+
14641473/* Get the thread state to a minimal consistent state.
14651474 Further init happens in pylifecycle.c before it can be used.
14661475 All fields not initialized here are expected to be zeroed out,
@@ -1925,8 +1934,12 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate)
19251934// Deletes the thread states in the linked list `list`.
19261935//
19271936// This is intended to be used in conjunction with _PyThreadState_RemoveExcept.
1937+ //
1938+ // If `is_after_fork` is true, the thread states are immediately freed.
1939+ // Otherwise, they are decref'd because they may still be referenced by an
1940+ // OS thread.
19281941void
1929- _PyThreadState_DeleteList (PyThreadState * list )
1942+ _PyThreadState_DeleteList (PyThreadState * list , int is_after_fork )
19301943{
19311944 // The world can't be stopped because we PyThreadState_Clear() can
19321945 // call destructors.
@@ -1936,7 +1949,12 @@ _PyThreadState_DeleteList(PyThreadState *list)
19361949 for (p = list ; p ; p = next ) {
19371950 next = p -> next ;
19381951 PyThreadState_Clear (p );
1939- free_threadstate ((_PyThreadStateImpl * )p );
1952+ if (is_after_fork ) {
1953+ free_threadstate ((_PyThreadStateImpl * )p );
1954+ }
1955+ else {
1956+ decref_threadstate ((_PyThreadStateImpl * )p );
1957+ }
19401958 }
19411959}
19421960
@@ -2069,12 +2087,19 @@ static void
20692087tstate_wait_attach (PyThreadState * tstate )
20702088{
20712089 do {
2072- int expected = _Py_THREAD_SUSPENDED ;
2073-
2074- // Wait until we're switched out of SUSPENDED to DETACHED.
2075- _PyParkingLot_Park (& tstate -> state , & expected , sizeof (tstate -> state ),
2076- /*timeout=*/ -1 , NULL , /*detach=*/ 0 );
2077-
2090+ int state = _Py_atomic_load_int_relaxed (& tstate -> state );
2091+ if (state == _Py_THREAD_SUSPENDED ) {
2092+ // Wait until we're switched out of SUSPENDED to DETACHED.
2093+ _PyParkingLot_Park (& tstate -> state , & state , sizeof (tstate -> state ),
2094+ /*timeout=*/ -1 , NULL , /*detach=*/ 0 );
2095+ }
2096+ else if (state == _Py_THREAD_SHUTTING_DOWN ) {
2097+ // We're shutting down, so we can't attach.
2098+ _PyThreadState_HangThread (tstate );
2099+ }
2100+ else {
2101+ assert (state == _Py_THREAD_DETACHED );
2102+ }
20782103 // Once we're back in DETACHED we can re-attach
20792104 } while (!tstate_try_attach (tstate ));
20802105}
@@ -2105,7 +2130,7 @@ _PyThreadState_Attach(PyThreadState *tstate)
21052130 tstate_activate (tstate );
21062131
21072132#ifdef Py_GIL_DISABLED
2108- if (_PyEval_IsGILEnabled (tstate ) && !tstate -> _status . holds_gil ) {
2133+ if (_PyEval_IsGILEnabled (tstate ) && !tstate -> holds_gil ) {
21092134 // The GIL was enabled between our call to _PyEval_AcquireLock()
21102135 // and when we attached (the GIL can't go from enabled to disabled
21112136 // here because only a thread holding the GIL can disable
@@ -2188,6 +2213,15 @@ _PyThreadState_Suspend(PyThreadState *tstate)
21882213 HEAD_UNLOCK (runtime );
21892214}
21902215
2216+ void
2217+ _PyThreadState_SetShuttingDown (PyThreadState * tstate )
2218+ {
2219+ _Py_atomic_store_int (& tstate -> state , _Py_THREAD_SHUTTING_DOWN );
2220+ #ifdef Py_GIL_DISABLED
2221+ _PyParkingLot_UnparkAll (& tstate -> state );
2222+ #endif
2223+ }
2224+
21912225// Decrease stop-the-world counter of remaining number of threads that need to
21922226// pause. If we are the final thread to pause, notify the requesting thread.
21932227static void
@@ -2988,43 +3022,27 @@ _PyThreadState_CheckConsistency(PyThreadState *tstate)
29883022#endif
29893023
29903024
2991- // Check if a Python thread must exit immediately, rather than taking the GIL
2992- // if Py_Finalize() has been called.
3025+ // Check if a Python thread must call _PyThreadState_HangThread(), rather than
3026+ // taking the GIL or attaching to the interpreter if Py_Finalize() has been
3027+ // called.
29933028//
29943029// When this function is called by a daemon thread after Py_Finalize() has been
2995- // called, the GIL does no longer exist.
2996- //
2997- // tstate can be a dangling pointer (point to freed memory): only tstate value
2998- // is used, the pointer is not deferenced.
3030+ // called, the GIL may no longer exist.
29993031//
30003032// tstate must be non-NULL.
30013033int
30023034_PyThreadState_MustExit (PyThreadState * tstate )
30033035{
3004- /* bpo-39877: Access _PyRuntime directly rather than using
3005- tstate->interp->runtime to support calls from Python daemon threads.
3006- After Py_Finalize() has been called, tstate can be a dangling pointer:
3007- point to PyThreadState freed memory. */
3008- unsigned long finalizing_id = _PyRuntimeState_GetFinalizingID (& _PyRuntime );
3009- PyThreadState * finalizing = _PyRuntimeState_GetFinalizing (& _PyRuntime );
3010- if (finalizing == NULL ) {
3011- // XXX This isn't completely safe from daemon thraeds,
3012- // since tstate might be a dangling pointer.
3013- finalizing = _PyInterpreterState_GetFinalizing (tstate -> interp );
3014- finalizing_id = _PyInterpreterState_GetFinalizingID (tstate -> interp );
3015- }
3016- // XXX else check &_PyRuntime._main_interpreter._initial_thread
3017- if (finalizing == NULL ) {
3018- return 0 ;
3019- }
3020- else if (finalizing == tstate ) {
3021- return 0 ;
3022- }
3023- else if (finalizing_id == PyThread_get_thread_ident ()) {
3024- /* gh-109793: we must have switched interpreters. */
3025- return 0 ;
3026- }
3027- return 1 ;
3036+ int state = _Py_atomic_load_int_relaxed (& tstate -> state );
3037+ return state == _Py_THREAD_SHUTTING_DOWN ;
3038+ }
3039+
3040+ void
3041+ _PyThreadState_HangThread (PyThreadState * tstate )
3042+ {
3043+ _PyThreadStateImpl * tstate_impl = (_PyThreadStateImpl * )tstate ;
3044+ decref_threadstate (tstate_impl );
3045+ PyThread_hang_thread ();
30283046}
30293047
30303048/********************/
0 commit comments