Skip to content

Commit fff3356

Browse files
Mark threads as "done" *before* GC is finalized.
1 parent e3debdb commit fff3356

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

Python/pylifecycle.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1133,16 +1133,16 @@ Py_FinalizeEx(void)
11331133
return status;
11341134
}
11351135

1136+
/* Get current thread state and interpreter pointer */
1137+
PyThreadState *tstate = _PyThreadState_GET();
1138+
PyInterpreterState *interp = tstate->interp;
1139+
11361140
// Wrap up existing "threading"-module-created, non-daemon threads.
11371141
wait_for_thread_shutdown();
11381142

11391143
// Make any remaining pending calls.
11401144
_Py_FinishPendingCalls();
11411145

1142-
/* Get current thread state and interpreter pointer */
1143-
PyThreadState *tstate = _PyThreadState_GET();
1144-
PyInterpreterState *interp = tstate->interp;
1145-
11461146
/* The interpreter is still entirely intact at this point, and the
11471147
* exit funcs may be relying on that. In particular, if some thread
11481148
* or exit func is still waiting to do an import, the import machinery
@@ -2194,6 +2194,9 @@ wait_for_thread_shutdown(void)
21942194
Py_DECREF(result);
21952195
}
21962196
Py_DECREF(threading);
2197+
2198+
// All threading module threads are marked as "done" later
2199+
// in PyThreadState_Clear().
21972200
}
21982201

21992202
#define NEXITFUNCS 32

Python/pystate.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,13 @@ PyThreadState_Clear(PyThreadState *tstate)
781781
Py_CLEAR(tstate->async_gen_finalizer);
782782

783783
Py_CLEAR(tstate->context);
784+
785+
if (tstate->on_delete != NULL) {
786+
// This will unblock any joining threads.
787+
tstate->on_delete(tstate->on_delete_data);
788+
tstate->on_delete = NULL;
789+
tstate->on_delete_data = NULL;
790+
}
784791
}
785792

786793

@@ -804,6 +811,8 @@ tstate_delete_common(_PyRuntimeState *runtime, PyThreadState *tstate)
804811
tstate->next->prev = tstate->prev;
805812
HEAD_UNLOCK(runtime);
806813
if (tstate->on_delete != NULL) {
814+
// This will unblock any joining threads.
815+
// We also do this in PyThreadState_Clear(), but do it here to be sure.
807816
tstate->on_delete(tstate->on_delete_data);
808817
}
809818
PyMem_RawFree(tstate);

0 commit comments

Comments
 (0)