You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On Windows, if a multiprocessing.Process fails to spawn, and it is given multiprocessing.Pipe(duplex=True) arguments, then the underlying handles pipe handles leak. One way to cause a subprocess to fail to spawn is forgetting to guard spawning code with if __name__ == "__main__":. Here is an MRE along those lines:
I noticed this because it makes the death of the process impossible to detect by looking only at the pipes, leading to an ugly workaround that I only recently realized was actually leaking resources.
This is in principle the same bug as bpo-33929. To be honest I don't understand the fix there enough to know if it could be generalized to PipeConnection objects, but it would likely prevent leaks from all sorts of handle stealing edge cases. Otherwise, workaround for the specific case of recursive spawning could be achieved by signaling the unpickling error back to parent during the Process.start() method. The state of the system as the MRE failure occurs can be summarized as:
child raises and never unpickles the arguments and so never steals the handle via DupeHandle.detach()
parent calls poll on read end of PipeConnection
parent never gets expected BrokenPipeError
If Process.start can raise and close from_parent (actually I'm not sure if from_parent or fd or both must be closed) in child before dump(process_obj, to_child) in parent, all stealing leaks would prevented. However, that would require some sort of IO wait between line 94 and 95. My first thought was to make another private pipe pair only for the purpose of signaling parent if prep_data were successfully unpickled or not. parent would then either read a sentinel out of the pipe or raise some kind of exception. Also, wouldn't it be better in principle for start to raise an exception and clean up if it knows it failed to start?
Bug report
Bug description:
On Windows, if a
multiprocessing.Processfails to spawn, and it is givenmultiprocessing.Pipe(duplex=True)arguments, then the underlying handles pipe handles leak. One way to cause a subprocess to fail to spawn is forgetting to guard spawning code withif __name__ == "__main__":. Here is an MRE along those lines:I noticed this because it makes the death of the process impossible to detect by looking only at the pipes, leading to an ugly workaround that I only recently realized was actually leaking resources.
This is in principle the same bug as bpo-33929. To be honest I don't understand the fix there enough to know if it could be generalized to
PipeConnectionobjects, but it would likely prevent leaks from all sorts of handle stealing edge cases. Otherwise, workaround for the specific case of recursive spawning could be achieved by signaling the unpickling error back toparentduring theProcess.start()method. The state of the system as the MRE failure occurs can be summarized as:parentcallsProcess.start->self._Popen(self)->CreateProcessparentdumpsprep_dataand thenprocess_objsequentiallyto_childwith big buffer, so it's nonblockingprocess_objinduces duplication ofPipeConnectionhandle viaDupeHandlechildrunsmp.spawn.spawn_main->_mainchilddrains data (fdfrom_parentof bpo-33929: multiprocessing: fix handle leak on race condition #7921) only toreduction.pickle.load(from_parent)childunpickles__main__modulechildnotices issue inProcess.start->self._Popen(self)->spawn.get_preparation_data->_check_not_importing_mainchildraises and never unpickles the arguments and so never steals the handle viaDupeHandle.detach()parentcallspollon read end ofPipeConnectionparentnever gets expectedBrokenPipeErrorIf
Process.startcan raise and closefrom_parent(actually I'm not sure iffrom_parentorfdor both must be closed) inchildbeforedump(process_obj, to_child)inparent, all stealing leaks would prevented. However, that would require some sort of IO wait between line 94 and 95. My first thought was to make another private pipe pair only for the purpose of signalingparentifprep_datawere successfully unpickled or not.parentwould then either read a sentinel out of the pipe or raise some kind of exception. Also, wouldn't it be better in principle forstartto raise an exception and clean up if it knows it failed to start?CPython versions tested on:
3.8, 3.9, 3.10, 3.11, 3.12
Operating systems tested on:
Windows