Skip to content

Commit 1fa9644

Browse files
GH-96071: fix deadlock in PyGILState_Ensure (GH-96124) (#96129)
Alternative of GH-96107 (cherry picked from commit e0d54a4) Co-authored-by: Kumar Aditya <[email protected]> Co-authored-by: Kumar Aditya <[email protected]>
1 parent fcf0421 commit 1fa9644

File tree

2 files changed

+17
-11
lines changed

2 files changed

+17
-11
lines changed
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.

Diff for: Python/pystate.c

+16-11
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,15 @@ new_threadstate(PyInterpreterState *interp)
795795
{
796796
PyThreadState *tstate;
797797
_PyRuntimeState *runtime = interp->runtime;
798-
798+
// We don't need to allocate a thread state for the main interpreter
799+
// (the common case), but doing it later for the other case revealed a
800+
// reentrancy problem (deadlock). So for now we always allocate before
801+
// taking the interpreters lock. See GH-96071.
802+
PyThreadState *new_tstate = alloc_threadstate();
803+
int used_newtstate;
804+
if (new_tstate == NULL) {
805+
return NULL;
806+
}
799807
/* We serialize concurrent creation to protect global state. */
800808
HEAD_LOCK(runtime);
801809

@@ -807,18 +815,15 @@ new_threadstate(PyInterpreterState *interp)
807815
if (old_head == NULL) {
808816
// It's the interpreter's initial thread state.
809817
assert(id == 1);
810-
818+
used_newtstate = 0;
811819
tstate = &interp->_initial_thread;
812820
}
813821
else {
814822
// Every valid interpreter must have at least one thread.
815823
assert(id > 1);
816824
assert(old_head->prev == NULL);
817-
818-
tstate = alloc_threadstate();
819-
if (tstate == NULL) {
820-
goto error;
821-
}
825+
used_newtstate = 1;
826+
tstate = new_tstate;
822827
// Set to _PyThreadState_INIT.
823828
memcpy(tstate,
824829
&initial._main_interpreter._initial_thread,
@@ -829,11 +834,11 @@ new_threadstate(PyInterpreterState *interp)
829834
init_threadstate(tstate, interp, id, old_head);
830835

831836
HEAD_UNLOCK(runtime);
837+
if (!used_newtstate) {
838+
// Must be called with lock unlocked to avoid re-entrancy deadlock.
839+
PyMem_RawFree(new_tstate);
840+
}
832841
return tstate;
833-
834-
error:
835-
HEAD_UNLOCK(runtime);
836-
return NULL;
837842
}
838843

839844
PyThreadState *

0 commit comments

Comments
 (0)