-
-
Notifications
You must be signed in to change notification settings - Fork 31.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
starting a thread in __del__ hangs at interpreter shutdown #87950
Comments
I've noticed an issue (or user error) in which Python a call that otherwise usually works in the __del__ step of a class will freeze when the Python interpreter is exiting. I've attached sample code that I've ran against Python 3.9.1 on Windows 10. The code below runs a process and communicates via the pipe. class SubprocTest(object):
def run(self):
print("run")
proc_args = ["cmd.exe"]
self._process = subprocess.Popen(proc_args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
def __del__(self):
print("__del__")
if self._process is not None:
self.terminate()
def terminate(self):
print("terminate")
self._process.communicate(input=b"exit\n", timeout=1)
print("kill")
self._process.kill()
self._process = None
if __name__ == "__main__":
s = SubprocTest()
s.run()
del s
print("s done")
t = SubprocTest()
t.run()
print("t done") Current output: Expected output: In normal circumstances, when you del the object and force a run of __del__(), the process ends properly and the terminate() method completes. When the Python interpreter exits, Python calls the __del__() method of the class. In this case, the terminate() never completes and the script freezes indefinitely on the communicate() |
It's not a subprocess bug, per se. It's due to creating the stdout/stderr worker threads from the __del__ finalizer while the interpreter is shutting down. Minimal reproducer, confirmed in both Linux and Windows: import threading
class C:
def __del__(self):
t = threading.Thread(target=print, args=('spam',), daemon=True)
t.start()
c = C()
#del c # uncomment to prevent hanging |
eryksun, wow, that's speedy analysis, but there might be more to it. I went and tested a bunch of test cases. my subrocess code doesn't seem to hang on Linux where the thread example code does? Linux - Python 3.6.8 - your threading example DOESN'T hang Linux - Python 3.8.5 - thread example HANGs Windows - Python 3.9.1 - thread example HANGs |
Reader threads for stdout and stderr are only used in Windows, since there's no equivalent to select/poll for synchronous pipes in Windows. Without using threads, I/O with synchronous pipes requires a busy loop. It has to poll PeekNamedPipe() to get the number of bytes available to read without blocking. For stdin, on the other hand, the Windows API does not allow getting the WriteQuotaAvailable from the PipeLocalInformation [1]. Without knowing how much can be written without blocking, the input pipe would have to be made non-blocking, which in turn requires an inner loop that tries to write a successively smaller chunk size to stdin until either it succeeds or the size is reduced to 0. If we wanted to change communicate() in Windows to not use threads and not require a busy loop, we'd have to switch to using named pipes opened in asynchronous (overlapped) mode on our side of each pipe. But that's an integration problem. For normal use as proc.stdout, etc, we would need an adapter or a new raw file type that implements a synchronous interface by waiting for I/O completion and maintaining its own file pointer. Such files would return the Windows file handle for fileno(), like sockets do, since a C file descriptor requires a synchronous-mode file. --- |
Hi, just curious, how far down in the backlog is this issue, and how likely would it to get fixed? It still exhibits this behavior on the most recent releases |
Adding |
It's not Windows-specific, apart from the fact that A threading/state expert is needed to figure out the best way to cause thread creation to fail during finalization (either object or interpreter - I'm not sure which). And it's likely that this will still leave the original code broken, just more quickly and with an unhandled exception. |
🙄 Looks like this is a truly complex issue. Anyways, I think an unhandled exception is preferred to hanging the interpreter. At least that way, control flow can continue... like an external caller to run a python script doesn't wait indefinitely, but can read an error code from the interpreter crash |
#104826 from #104690 may be of interest... That was focusing on the |
Interesting issues referred. If the PR #104826 goes through I'd be curious if it'd be a partial fix... possibly. Afterall, it touches |
So, I tested the code chunk from #87950 (comment) on CPython 3.12.0 on Windows x64, and it definitely doesn't hang anymore.
It crashes with
Which is much more ideal. I can now trap this error and do additional processing. Is it possible to backport this to older versions, or is this issue considered resolved but only for v3.12.0+? |
The details on this kind of change are often quite a challenge to get right. We're not likely to backport any changes that may have helped fix this to 3.11.x (which is going to move to security fix only status soon anyways). (keeping open for now as this being fixed deserves further verification.) |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: