diff options
Diffstat (limited to 'Python/pystate.c')
-rw-r--r-- | Python/pystate.c | 96 |
1 files changed, 57 insertions, 39 deletions
diff --git a/Python/pystate.c b/Python/pystate.c index 4b01942f40e..fcd12d1b933 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1474,6 +1474,15 @@ free_threadstate(_PyThreadStateImpl *tstate) } } +static void +decref_threadstate(_PyThreadStateImpl *tstate) +{ + if (_Py_atomic_add_ssize(&tstate->refcount, -1) == 1) { + // The last reference to the thread state is gone. + free_threadstate(tstate); + } +} + /* Get the thread state to a minimal consistent state. Further init happens in pylifecycle.c before it can be used. All fields not initialized here are expected to be zeroed out, @@ -1938,8 +1947,12 @@ _PyThreadState_RemoveExcept(PyThreadState *tstate) // Deletes the thread states in the linked list `list`. // // This is intended to be used in conjunction with _PyThreadState_RemoveExcept. +// +// If `is_after_fork` is true, the thread states are immediately freed. +// Otherwise, they are decref'd because they may still be referenced by an +// OS thread. void -_PyThreadState_DeleteList(PyThreadState *list) +_PyThreadState_DeleteList(PyThreadState *list, int is_after_fork) { // The world can't be stopped because we PyThreadState_Clear() can // call destructors. @@ -1949,7 +1962,12 @@ _PyThreadState_DeleteList(PyThreadState *list) for (p = list; p; p = next) { next = p->next; PyThreadState_Clear(p); - free_threadstate((_PyThreadStateImpl *)p); + if (is_after_fork) { + free_threadstate((_PyThreadStateImpl *)p); + } + else { + decref_threadstate((_PyThreadStateImpl *)p); + } } } @@ -2082,12 +2100,19 @@ static void tstate_wait_attach(PyThreadState *tstate) { do { - int expected = _Py_THREAD_SUSPENDED; - - // Wait until we're switched out of SUSPENDED to DETACHED. - _PyParkingLot_Park(&tstate->state, &expected, sizeof(tstate->state), - /*timeout=*/-1, NULL, /*detach=*/0); - + int state = _Py_atomic_load_int_relaxed(&tstate->state); + if (state == _Py_THREAD_SUSPENDED) { + // Wait until we're switched out of SUSPENDED to DETACHED. + _PyParkingLot_Park(&tstate->state, &state, sizeof(tstate->state), + /*timeout=*/-1, NULL, /*detach=*/0); + } + else if (state == _Py_THREAD_SHUTTING_DOWN) { + // We're shutting down, so we can't attach. + _PyThreadState_HangThread(tstate); + } + else { + assert(state == _Py_THREAD_DETACHED); + } // Once we're back in DETACHED we can re-attach } while (!tstate_try_attach(tstate)); } @@ -2118,7 +2143,7 @@ _PyThreadState_Attach(PyThreadState *tstate) tstate_activate(tstate); #ifdef Py_GIL_DISABLED - if (_PyEval_IsGILEnabled(tstate) && !tstate->_status.holds_gil) { + if (_PyEval_IsGILEnabled(tstate) && !tstate->holds_gil) { // The GIL was enabled between our call to _PyEval_AcquireLock() // and when we attached (the GIL can't go from enabled to disabled // here because only a thread holding the GIL can disable @@ -2201,6 +2226,15 @@ _PyThreadState_Suspend(PyThreadState *tstate) HEAD_UNLOCK(runtime); } +void +_PyThreadState_SetShuttingDown(PyThreadState *tstate) +{ + _Py_atomic_store_int(&tstate->state, _Py_THREAD_SHUTTING_DOWN); +#ifdef Py_GIL_DISABLED + _PyParkingLot_UnparkAll(&tstate->state); +#endif +} + // Decrease stop-the-world counter of remaining number of threads that need to // pause. If we are the final thread to pause, notify the requesting thread. static void @@ -3001,43 +3035,27 @@ _PyThreadState_CheckConsistency(PyThreadState *tstate) #endif -// Check if a Python thread must exit immediately, rather than taking the GIL -// if Py_Finalize() has been called. +// Check if a Python thread must call _PyThreadState_HangThread(), rather than +// taking the GIL or attaching to the interpreter if Py_Finalize() has been +// called. // // When this function is called by a daemon thread after Py_Finalize() has been -// called, the GIL does no longer exist. -// -// tstate can be a dangling pointer (point to freed memory): only tstate value -// is used, the pointer is not deferenced. +// called, the GIL may no longer exist. // // tstate must be non-NULL. int _PyThreadState_MustExit(PyThreadState *tstate) { - /* bpo-39877: Access _PyRuntime directly rather than using - tstate->interp->runtime to support calls from Python daemon threads. - After Py_Finalize() has been called, tstate can be a dangling pointer: - point to PyThreadState freed memory. */ - unsigned long finalizing_id = _PyRuntimeState_GetFinalizingID(&_PyRuntime); - PyThreadState *finalizing = _PyRuntimeState_GetFinalizing(&_PyRuntime); - if (finalizing == NULL) { - // XXX This isn't completely safe from daemon thraeds, - // since tstate might be a dangling pointer. - finalizing = _PyInterpreterState_GetFinalizing(tstate->interp); - finalizing_id = _PyInterpreterState_GetFinalizingID(tstate->interp); - } - // XXX else check &_PyRuntime._main_interpreter._initial_thread - if (finalizing == NULL) { - return 0; - } - else if (finalizing == tstate) { - return 0; - } - else if (finalizing_id == PyThread_get_thread_ident()) { - /* gh-109793: we must have switched interpreters. */ - return 0; - } - return 1; + int state = _Py_atomic_load_int_relaxed(&tstate->state); + return state == _Py_THREAD_SHUTTING_DOWN; +} + +void +_PyThreadState_HangThread(PyThreadState *tstate) +{ + _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate; + decref_threadstate(tstate_impl); + PyThread_hang_thread(); } /********************/ |