Skip to content

Commit e0d54a4

Browse files
GH-96071: fix deadlock in PyGILState_Ensure (GH-96124)
Alternative of #96107
1 parent 822955c commit e0d54a4

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix a deadlock in :c:func:`PyGILState_Ensure` when allocating new thread state. Patch by Kumar Aditya.

Python/pystate.c

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,15 @@ new_threadstate(PyInterpreterState *interp)
810810
{
811811
PyThreadState *tstate;
812812
_PyRuntimeState *runtime = interp->runtime;
813-
813+
// We don't need to allocate a thread state for the main interpreter
814+
// (the common case), but doing it later for the other case revealed a
815+
// reentrancy problem (deadlock). So for now we always allocate before
816+
// taking the interpreters lock. See GH-96071.
817+
PyThreadState *new_tstate = alloc_threadstate();
818+
int used_newtstate;
819+
if (new_tstate == NULL) {
820+
return NULL;
821+
}
814822
/* We serialize concurrent creation to protect global state. */
815823
HEAD_LOCK(runtime);
816824

@@ -822,18 +830,15 @@ new_threadstate(PyInterpreterState *interp)
822830
if (old_head == NULL) {
823831
// It's the interpreter's initial thread state.
824832
assert(id == 1);
825-
833+
used_newtstate = 0;
826834
tstate = &interp->_initial_thread;
827835
}
828836
else {
829837
// Every valid interpreter must have at least one thread.
830838
assert(id > 1);
831839
assert(old_head->prev == NULL);
832-
833-
tstate = alloc_threadstate();
834-
if (tstate == NULL) {
835-
goto error;
836-
}
840+
used_newtstate = 1;
841+
tstate = new_tstate;
837842
// Set to _PyThreadState_INIT.
838843
memcpy(tstate,
839844
&initial._main_interpreter._initial_thread,
@@ -844,11 +849,11 @@ new_threadstate(PyInterpreterState *interp)
844849
init_threadstate(tstate, interp, id, old_head);
845850

846851
HEAD_UNLOCK(runtime);
852+
if (!used_newtstate) {
853+
// Must be called with lock unlocked to avoid re-entrancy deadlock.
854+
PyMem_RawFree(new_tstate);
855+
}
847856
return tstate;
848-
849-
error:
850-
HEAD_UNLOCK(runtime);
851-
return NULL;
852857
}
853858

854859
PyThreadState *

0 commit comments

Comments
 (0)