diff --git a/ansible_mitogen/process.py b/ansible_mitogen/process.py index c9247fda..a87d6a34 100644 --- a/ansible_mitogen/process.py +++ b/ansible_mitogen/process.py @@ -46,17 +46,20 @@ import ansible_mitogen.services class MuxProcess(object): """ - This implements a process forked from the Ansible top-level process as a - safe place to contain the Mitogen IO multiplexer thread, keeping its use of - the logging package (and the logging package's heavy use of locks) far away - from the clutches of os.fork(), which is used continuously in the top-level - process. + Implement a subprocess forked from the Ansible top-level, as a safe place + to contain the Mitogen IO multiplexer thread, keeping its use of the + logging package (and the logging package's heavy use of locks) far away + from the clutches of os.fork(), which is used continuously by the + multiprocessing package in the top-level process. The problem with running the multiplexer in that process is that should the multiplexer thread be in the process of emitting a log entry (and holding its lock) at the point of fork, in the child, the first attempt to log any log entry using the same handler will deadlock the child, as in the memory image the child received, the lock will always be marked held. + + See https://bugs.python.org/issue6721 for a thorough description of the + class of problems this worker is intended to avoid. """ #: In the top-level process, this references one end of a socketpair(), @@ -82,6 +85,13 @@ class MuxProcess(object): @classmethod def start(cls): + """ + Arrange for the subprocess to be started, if it is not already running. + + The parent process picks a UNIX socket path the child will use prior to + fork, creates a socketpair used essentially as a semaphore, then blocks + waiting for the child to indicate the UNIX socket is ready for use. + """ if cls.worker_sock is not None: return @@ -100,18 +110,22 @@ class MuxProcess(object): cls.worker_sock.close() cls.worker_sock = None self = cls() - self.run() + self.worker_main() sys.exit() - def run(self): + def worker_main(self): + """ + The main function of for the mux process: setup the Mitogen broker + thread and ansible_mitogen services, then sleep waiting for the socket + connected to the parent to be closed (indicating the parent has died). + """ self._setup_master() self._setup_services() + + # Let the parent know our listening socket is ready. self.child_sock.send('1') - try: - self.child_sock.recv(1) - except Exception, e: - print 'do e', e - pass + # Block until the socket is closed, which happens on parent exit. + self.child_sock.recv(1) def _setup_master(self): """