diff options
-rw-r--r-- | Misc/NEWS.d/next/Core and Builtins/2022-09-16-16-54-35.gh-issue-96387.GRzewg.rst | 5 | ||||
-rw-r--r-- | Python/ceval_gil.c | 11 |
2 files changed, 16 insertions, 0 deletions
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-09-16-16-54-35.gh-issue-96387.GRzewg.rst b/Misc/NEWS.d/next/Core and Builtins/2022-09-16-16-54-35.gh-issue-96387.GRzewg.rst new file mode 100644 index 0000000..611ab94 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-09-16-16-54-35.gh-issue-96387.GRzewg.rst @@ -0,0 +1,5 @@ +At Python exit, sometimes a thread holding the GIL can wait forever for a +thread (usually a daemon thread) which requested to drop the GIL, whereas +the thread already exited. To fix the race condition, the thread which +requested the GIL drop now resets its request before exiting. Issue +discovered and analyzed by Mingliang ZHAO. Patch by Victor Stinner. diff --git a/Python/ceval_gil.c b/Python/ceval_gil.c index a679086..fd737b5 100644 --- a/Python/ceval_gil.c +++ b/Python/ceval_gil.c @@ -369,6 +369,7 @@ take_gil(PyThreadState *tstate) goto _ready; } + int drop_requested = 0; while (_Py_atomic_load_relaxed(&gil->locked)) { unsigned long saved_switchnum = gil->switch_number; @@ -384,11 +385,21 @@ take_gil(PyThreadState *tstate) { if (tstate_must_exit(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 + // requests made by other threads are also reset: these threads + // may have to request again a drop request (iterate one more + // time). + if (drop_requested) { + RESET_GIL_DROP_REQUEST(interp); + } PyThread_exit_thread(); } assert(is_tstate_valid(tstate)); SET_GIL_DROP_REQUEST(interp); + drop_requested = 1; } } |