summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2022-09-16-16-54-35.gh-issue-96387.GRzewg.rst5
-rw-r--r--Python/ceval_gil.c11
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;
}
}