-
-
Notifications
You must be signed in to change notification settings - Fork 30.4k
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
garbage collection just after multiprocessing's fork causes exceptions #58753
Comments
When running test_multiprocessing on Linux I occasionally see a stream of errors caused by ignored weakref callbacks: Exception AssertionError: AssertionError() in <Finalize object, dead> ignored These do not cause the unittests to fail. Finalizers from the parent process are supposed to be cleared after the fork. But if a garbage collection before that then Finalizer callbacks can be run in the "wrong" process. Disabling gc during fork seems to prevent the errors. Or maybe the Finalizer should record the pid of the process which created it and only invoke the callback if it matches the current pid. (Compare Issure 1336 conscerning subprocess.) |
Sounds reasonable to me. |
Patch to disable gc. |
Shouldn't there be a try..finally, in case os.fork() fails? |
Hmm... |
That's a problem indeed. Perhaps we need a global "fork lock" shared |
I did an atfork patch which included a (recursive) fork lock. See
The patch included changes to multiprocessing and subprocess. (Being able to acquire the lock when doing fd manipulation is quite useful. For instance, the creation of Process.sentinel currently has a race which can mean than another process inherits the write end of the pipe. That would cause Process.join() to wait till both processes terminate.) Actually, for Finalizers I think it would be easier to just record and check the pid. |
Indeed, I had a look and it looked good.
I'd prefer this too. |
Alternative patch which records pid when Finalize object is created. The callback does nothing if recorded pid does not match os.getpid(). |
But what if Finalize is used to cleanup a resource that gets duplicated in children, like a file descriptor? |
This was how Finalize objects already acted (or were supposed to). In the case of BufferWrapper this is intended. BufferWrapper objects do not have reference counting semantics. Instead the memory is deallocated when the object is garbage collected in the process that created it. (Garbage collection in a child process should *not* invalidate memory owned by the parent process.) You can prevent the parent process from garbage collecting the object too early by following the advice below from the documentation: Explicitly pass resources to child processes
In the case of the sentinel in Popen.__init__(), it is harmless if this end of the pipe gets accidentally inherited by another process. Since Process does not have a closefds argument like subprocess.Popen unintended leaking happens all the time. And even without the pid check, I think this finalizer would very rarely be triggered in a child process. (A Process object can only be garbage collected after it has been joined, and it can only be joined by it parent process.) |
Looks good to me. |
New changeset 59567c117b0e by Richard Oudkerk in branch 'default': |
New changeset 751371dd4d1c by Richard Oudkerk in branch '2.7': |
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: