Skip to content

Commit 04f4977

Browse files
authored
gh-96387: take_gil() resets drop request before exit (#96869)
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. take_gil() now calls RESET_GIL_DROP_REQUEST() before PyThread_exit_thread() if it called SET_GIL_DROP_REQUEST to fix a race condition with drop_gil(). Issue discovered and analyzed by Mingliang ZHAO.
1 parent c10e33a commit 04f4977

File tree

2 files changed

+16
-0
lines changed

2 files changed

+16
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
At Python exit, sometimes a thread holding the GIL can wait forever for a
2+
thread (usually a daemon thread) which requested to drop the GIL, whereas
3+
the thread already exited. To fix the race condition, the thread which
4+
requested the GIL drop now resets its request before exiting. Issue
5+
discovered and analyzed by Mingliang ZHAO. Patch by Victor Stinner.

Python/ceval_gil.c

+11
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ take_gil(PyThreadState *tstate)
369369
goto _ready;
370370
}
371371

372+
int drop_requested = 0;
372373
while (_Py_atomic_load_relaxed(&gil->locked)) {
373374
unsigned long saved_switchnum = gil->switch_number;
374375

@@ -384,11 +385,21 @@ take_gil(PyThreadState *tstate)
384385
{
385386
if (tstate_must_exit(tstate)) {
386387
MUTEX_UNLOCK(gil->mutex);
388+
// gh-96387: If the loop requested a drop request in a previous
389+
// iteration, reset the request. Otherwise, drop_gil() can
390+
// block forever waiting for the thread which exited. Drop
391+
// requests made by other threads are also reset: these threads
392+
// may have to request again a drop request (iterate one more
393+
// time).
394+
if (drop_requested) {
395+
RESET_GIL_DROP_REQUEST(interp);
396+
}
387397
PyThread_exit_thread();
388398
}
389399
assert(is_tstate_valid(tstate));
390400

391401
SET_GIL_DROP_REQUEST(interp);
402+
drop_requested = 1;
392403
}
393404
}
394405

0 commit comments

Comments
 (0)