BaseSubprocessTransport.__del__
fails if the event loop is already closed, which can leak an orphan process
#114177
Labels
type-bug
An unexpected behavior, bug, or error
Bug report
Bug description:
there is a race where it's possible for
BaseSubprocessTransport.__del__
to try to close the transport after the event loop has been closed. this results in an unraisable exception in__del__
, and it can also result in an orphan process being leaked.the following is a reproducer that triggers the race between
run()
exiting and [the process dying and the event loop learning about the process's death]. on my machine, with this reproducer the bug occurs (due torun()
winning the race) maybe 90% of the time:most of the time, running this emits
this case looks similar to GH-109538. i think the following patch (analogous to GH-111983) fixes it:
however, there is another case for which the above patch is not sufficient. in the above example the user orphaned the process after sending
SIGKILL
/TerminateProcess
(which is not immediate, but only schedules the kill), but what if they fully orphan it?currently (on
main
), when the race condition occurs (for this example the condition isrun()
winning the race againstBaseSubprocessTransport
GC) then asyncio emits a loud complaintException ignored in: <function BaseSubprocessTransport.__del__ at 0x7f5b3b291e40>
and leaks the orphan process (checkhtop
after the interpreter exits!). asyncio probably also leaks the pipes.but with the patch above, asyncio will quietly leak the orphan process (and probably pipes), but it will not yell about the leak unless the user enables
ResourceWarning
s. which is not good.so a more correct patch (fixes both cases) may be something along the lines of
with this patch applied, neither example leaks an orphan process out of
run()
, and both examples emitResourceWarning
. however this patch is rather messy. it is also perhaps still leaking pipe fd's out ofrun()
. (the fd's probably get closed by the OS when the interpreter shuts down, but i suspect one end of each pipe will be an orphan from the time whenrun()
exits to the time when the interpreter shuts down, which can be arbitrarily long).CPython versions tested on:
3.11, CPython main branch
Operating systems tested on:
Linux
The text was updated successfully, but these errors were encountered: