aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/pystate.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/pystate.c')
-rw-r--r--Python/pystate.c96
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();
}
/********************/