Skip to content

Commit 1e1301d

Browse files
committed
Only check for non-daemon threads at finalization.
Since threading._shutdown() only shuts down non-daemon threads, daemon threads would cause the loop to run indefinitely.
1 parent f1460af commit 1e1301d

File tree

4 files changed

+25
-5
lines changed

4 files changed

+25
-5
lines changed

Include/cpython/pystate.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ struct _ts {
107107
# define _PyThreadState_WHENCE_THREADING 3
108108
# define _PyThreadState_WHENCE_GILSTATE 4
109109
# define _PyThreadState_WHENCE_EXEC 5
110+
# define _PyThreadState_WHENCE_THREADING_DAEMON 6
110111
#endif
111112

112113
/* Currently holds the GIL. Must be its own field to avoid data races */

Modules/_threadmodule.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ force_done(void *arg)
415415

416416
static int
417417
ThreadHandle_start(ThreadHandle *self, PyObject *func, PyObject *args,
418-
PyObject *kwargs)
418+
PyObject *kwargs, int daemon)
419419
{
420420
// Mark the handle as starting to prevent any other threads from doing so
421421
PyMutex_Lock(&self->mutex);
@@ -439,7 +439,8 @@ ThreadHandle_start(ThreadHandle *self, PyObject *func, PyObject *args,
439439
goto start_failed;
440440
}
441441
PyInterpreterState *interp = _PyInterpreterState_GET();
442-
boot->tstate = _PyThreadState_New(interp, _PyThreadState_WHENCE_THREADING);
442+
uint8_t whence = daemon ? _PyThreadState_WHENCE_THREADING_DAEMON : _PyThreadState_WHENCE_THREADING;
443+
boot->tstate = _PyThreadState_New(interp, whence);
443444
if (boot->tstate == NULL) {
444445
PyMem_RawFree(boot);
445446
if (!PyErr_Occurred()) {
@@ -1874,7 +1875,7 @@ do_start_new_thread(thread_module_state *state, PyObject *func, PyObject *args,
18741875
add_to_shutdown_handles(state, handle);
18751876
}
18761877

1877-
if (ThreadHandle_start(handle, func, args, kwargs) < 0) {
1878+
if (ThreadHandle_start(handle, func, args, kwargs, daemon) < 0) {
18781879
if (!daemon) {
18791880
remove_from_shutdown_handles(handle);
18801881
}

Python/pylifecycle.c

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2006,15 +2006,32 @@ resolve_final_tstate(_PyRuntimeState *runtime)
20062006
static int
20072007
interp_has_threads(PyInterpreterState *interp)
20082008
{
2009+
/* This needs to check for non-daemon threads only, otherwise we get stuck
2010+
* in an infinite loop. */
20092011
assert(interp != NULL);
2012+
assert(interp->runtime->stoptheworld.world_stopped);
20102013
assert(interp->threads.head != NULL);
2011-
return interp->threads.head->next != NULL;
2014+
if (interp->threads.head->next == NULL) {
2015+
// No other threads active, easy way out.
2016+
return 0;
2017+
}
2018+
2019+
// We don't have to worry about locking this because the
2020+
// world is stopped.
2021+
_Py_FOR_EACH_TSTATE_UNLOCKED(interp, tstate) {
2022+
if (tstate->_whence == _PyThreadState_WHENCE_THREADING) {
2023+
return 1;
2024+
}
2025+
}
2026+
2027+
return 0;
20122028
}
20132029

20142030
static int
20152031
interp_has_pending_calls(PyInterpreterState *interp)
20162032
{
20172033
assert(interp != NULL);
2034+
assert(interp->runtime->stoptheworld.world_stopped);
20182035
return interp->ceval.pending.npending != 0;
20192036
}
20202037

@@ -2023,6 +2040,7 @@ interp_has_atexit_callbacks(PyInterpreterState *interp)
20232040
{
20242041
assert(interp != NULL);
20252042
assert(interp->atexit.callbacks != NULL);
2043+
assert(interp->runtime->stoptheworld.world_stopped);
20262044
assert(PyList_CheckExact(interp->atexit.callbacks));
20272045
return PyList_GET_SIZE(interp->atexit.callbacks) != 0;
20282046
}

Python/pystate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,7 @@ init_threadstate(_PyThreadStateImpl *_tstate,
14471447
assert(tstate->prev == NULL);
14481448

14491449
assert(tstate->_whence == _PyThreadState_WHENCE_NOTSET);
1450-
assert(whence >= 0 && whence <= _PyThreadState_WHENCE_EXEC);
1450+
assert(whence >= 0 && whence <= _PyThreadState_WHENCE_THREADING_DAEMON);
14511451
tstate->_whence = whence;
14521452

14531453
assert(id > 0);

0 commit comments

Comments
 (0)