Description
Bug report
Bug description:
Currently, "pre-finalization" (callbacks executed while the interpreter is still fully intact) looks like this:
Lines 2023 to 2038 in 642e5df
Threads are joined first, then pending calls are executed, and finally atexit callbacks are executed. The problem is that any of these three can create one another, such as a pending call creating a thread, or an atexit handler scheduling a pending call. The best way to demonstrate this is by creating a new thread inside of an atexit callback:
import atexit
import threading
import time
def run():
print(24)
time.sleep(1)
print(42)
@atexit.register
def start_thread():
threading.Thread(target=run).start()
On 3.13+ (and probably earlier), you'll get one of these three outputs:
24
is printed.- Nothing is printed.
- A fatal error occurs due to the stdout lock being unavailable.
This is because the thread created is incorrectly non-daemon, because the call to threading._shutdown
(which should join it) has already happened.
@ericsnowcurrently, I discussed this with you at the PyCon sprints. I think we came to the agreement that the best way to fix this is by looping until all three of them execute no callbacks.
CPython versions tested on:
CPython main branch
Operating systems tested on:
Linux