Repository navigation
Some pre-finalization callbacks can create other callbacks #136003
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or errorinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
on Jun 26, 2025 Yeah, this is what we discussed. Thanks for remembering and opening this issue. I have some thoughts, but first I'll take a look at your PR.
- added a commit that references this issue
on Sep 18, 2025 The test
test_pending_call_creates_thread_subinterpreterhas areference leak.
edit: a file descriptor leak. I'll send a fix.Sorry, @ZeroIntensity, I should have warned you about closing the file descriptors.
@ZeroIntensity, I do want to circle back on why
os.pipe()wasn't working in the test when we tried it that way.I should have warned you about closing the file descriptors.
No worries. For the future, it would be nice if regrtest was able to detect unclosed FDs automatically without running in refleak mode.
I do want to circle back on why os.pipe() wasn't working in the test when we tried it that way.
I'll experiment with it more later today. Maybe my laptop has some weird setting that prevents IPC with
pipe?Ah, @ericsnowcurrently, we need to use
pass_fdsfor the child process to inherit the fd:import subprocess import sys import os r, w = os.pipe() script = f"""if True: import os os.write({w}, b'a') """ subprocess.run([sys.executable, '-c', script], pass_fds=[w]) print(os.read(r, 1))
You may have been thinking of
multiprocessing.Pipe, which seems to do this for you.Reacted by Eric SnowFor the future, it would be nice if regrtest was able to detect unclosed FDs automatically without running in refleak mode.
That would be worth investigating. I would not be surprised if @vstinner or others have given this some consideration, but, even then, perhaps it's time to revisit it.
Checking for FD leaks in regular regrtest mode may slow down the code. At least, it's good that we have the refleak mode to check for FD leaks!
Reacted by Eric Snow
Bug report
Bug description:
Currently, "pre-finalization" (callbacks executed while the interpreter is still fully intact) looks like this:
cpython/Python/pylifecycle.c
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:
On 3.13+ (and probably earlier), you'll get one of these three outputs:
24is printed.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
Linked PRs