Description
Bug report
Bug description:
This is related to #113964, but trying to address the bigger problem of what "interpreter shutdown" is, and trying to make a stronger case for restoring the old behavior.
TL;DR: This was an undocumented change. The old behavior was useful. Documentation is contradictory. Users are struggling and are recommended to downgrade.
Undocumented change
In Python 3.11 and previous versions, daemon=False
threads could spawn new threads even after the main thread has exited.
This changed in Python 3.12, which throws "RuntimeError: can't create new thread at interpreter shutdown" when a daemon=False
thread tries to spawn new threads after the main thread has exited.
I could not find this change to be documented in 3.12 release notes, it appears to be an unintentional change.
Reproducer
"""Python 3.11: Successfully spawns 20 subthreads. Python 3.12: RuntimeError."""
from threading import Thread, current_thread
from time import sleep
def subthread():
print(f"Thread {current_thread().name} spawned...")
def thread():
for i in range(20):
Thread(target=subthread, daemon=False).start()
sleep(0.001)
Thread(target=thread, daemon=False).start()
Old behavior was useful
While not join()
ing the launched threads is considered an antipattern, at the very least the old behavior was useful for throw-away scripts that launch a bunch of threads, and have Python exit automatically once all work was finished.
Documentation is contradictory
The documentation says this about daemon threads:
A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left.
I think a useful interpretation of "exits when only daemon threads are left" also implies "the Python program starts shutting down when only daemon threads are left", and does not shut down before. Because what else could "exits" mean if not "shuts down"?
Although this is in conflict with the definition of "interpreter shutdown" that states:
The main reason for interpreter shutdown is that the
__main__
module or the script being run has finished executing.
This interpetation is also not being respected by sys.is_finalizing()
API. This is supposed to "Return True if the Python interpreter is shutting down", but it returns False in this situation even after the main thread has exited and thread spawning fails: #114570. That's another indication that this change was unintentional.
Users are struggling
Python has a reputation for breakage across version upgrades. I hope there is motivation among CPython developers to change that. I think such unintentional changes that break users, should be treated as regressions to be fixed.
There are three posts on StackOverflow [1] [2] [3], where downgrading Python is the dominant "solution". And when you have threads spawning more threads, the "proper fix" is often not a simple matter of adding a Thread.join()
somewhere, but may require re-architecting the whole program to keep track of what all threads are doing.
Related issues: #113964, #114570.
CPython versions tested on:
3.12.2, 3.11.7
Operating systems tested on:
Linux, macOS
Metadata
Metadata
Assignees
Labels
Projects
Status