diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-14-57-33.gh-issue-158090.0jWUTp.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-14-57-33.gh-issue-158090.0jWUTp.rst new file mode 100644 index 000000000000000..d892b90c537e1de --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-09-24-14-57-33.gh-issue-158090.0jWUTp.rst @@ -0,0 +1,2 @@ +Fix a race condition at interpreter shutdown where a daemon thread waiting +for the GIL could leave the main thread blocked forever in ``drop_gil()``. diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index 49b2ede4ed3e60f..891711e170a8c6c 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -343,7 +343,6 @@ take_gil(PyThreadState *tstate) PyThreadState *holder_tstate = (PyThreadState*)_Py_atomic_load_ptr_relaxed(&gil->last_holder); if (_PyThreadState_MustExit(tstate)) { - MUTEX_UNLOCK(gil->mutex); // gh-96387: If the loop requested a drop request in a previous // iteration, reset the request. Otherwise, drop_gil() can // block forever waiting for the thread which exited. Drop @@ -353,6 +352,7 @@ take_gil(PyThreadState *tstate) if (drop_requested) { _Py_unset_eval_breaker_bit(holder_tstate, _PY_GIL_DROP_REQUEST_BIT); } + MUTEX_UNLOCK(gil->mutex); // gh-87135: hang the thread as *thread_exit() is not a safe // API. It lacks stack unwind and local variable destruction. _PyThreadState_HangThread(tstate);