issue #155: fork.py v2, now with full resource cleanup

pull/167/head
David Wilson 7 years ago
parent 872181bebd
commit 2ea65420d0

@ -56,18 +56,23 @@ class Stream(mitogen.parent.Stream):
#: Reference to the importer, if any, recovered from the parent. #: Reference to the importer, if any, recovered from the parent.
importer = None importer = None
def construct(self, old_router, debug=False, profiling=False): #: User-supplied function for cleaning up child process state.
on_fork = None
def construct(self, old_router, on_fork=None, debug=False, profiling=False):
# fork method only supports a tiny subset of options. # fork method only supports a tiny subset of options.
super(Stream, self).construct(debug=debug, profiling=profiling) super(Stream, self).construct(debug=debug, profiling=profiling)
self.on_fork = on_fork
responder = getattr(old_router, 'responder', None) responder = getattr(old_router, 'responder', None)
if isinstance(responder, mitogen.parent.ModuleForwarder): if isinstance(responder, mitogen.parent.ModuleForwarder):
self.importer = responder.importer self.importer = responder.importer
name_prefix = 'fork'
def create_child(self, *_args): def create_child(self, *_args):
parentfp, childfp = mitogen.parent.create_socketpair() parentfp, childfp = mitogen.parent.create_socketpair()
self.pid = os.fork() self.pid = os.fork()
self.name = 'fork.' + str(self.pid)
if self.pid: if self.pid:
childfp.close() childfp.close()
# Decouple the socket from the lifetime of the Python socket object. # Decouple the socket from the lifetime of the Python socket object.
@ -79,18 +84,32 @@ class Stream(mitogen.parent.Stream):
self._child_main(childfp) self._child_main(childfp)
def _child_main(self, childfp): def _child_main(self, childfp):
# TODO: Latch descriptors inherited from the parent should be closed. mitogen.core.Latch._on_fork()
vars(mitogen.core._tls).clear() mitogen.core.Side._on_fork()
break_logging_locks() break_logging_locks()
if self.on_fork:
self.on_fork()
mitogen.core.set_block(childfp.fileno()) mitogen.core.set_block(childfp.fileno())
# Expected by the ExternalContext.main().
os.dup2(childfp.fileno(), 1) os.dup2(childfp.fileno(), 1)
os.dup2(childfp.fileno(), 100) os.dup2(childfp.fileno(), 100)
# Overwritten by ExternalContext.main(); we must replace the
# parent-inherited descriptors that were closed by Side._on_fork() to
# avoid ExternalContext.main() accidentally allocating new files over
# the standard handles.
os.dup2(childfp.fileno(), 0)
os.dup2(childfp.fileno(), 2)
childfp.close()
kwargs = self.get_main_kwargs() kwargs = self.get_main_kwargs()
kwargs['core_src_fd'] = None kwargs['core_src_fd'] = None
kwargs['importer'] = self.importer kwargs['importer'] = self.importer
kwargs['setup_package'] = False kwargs['setup_package'] = False
mitogen.core.ExternalContext().main(**kwargs) mitogen.core.ExternalContext().main(**kwargs)
sys.exit(0)
# Don't trigger atexit handlers, they were copied from the parent.
os._exit(0)
def _connect_bootstrap(self): def _connect_bootstrap(self):
# None required. # None required.

Loading…
Cancel
Save