|
@@ -426,11 +426,28 @@ class AsynPool(_pool.Pool):
|
|
|
self._timeout_handler, 'on_hard_timeout', noop,
|
|
|
)
|
|
|
|
|
|
- def _event_process_exit(self, hub, fd):
|
|
|
+ def _event_process_exit(self, hub, proc):
|
|
|
# This method is called whenever the process sentinel is readable.
|
|
|
- hub.remove(fd)
|
|
|
+ self._untrack_child_process(proc, hub)
|
|
|
self.maintain_pool()
|
|
|
|
|
|
+ def _track_child_process(self, proc, hub):
|
|
|
+ try:
|
|
|
+ fd = proc._sentinel_poll
|
|
|
+ except AttributeError:
|
|
|
+ # we need to duplicate the fd here to carefully
|
|
|
+ # control when the fd is removed from the process table,
|
|
|
+ # as once the original fd is closed we cannot unregister
|
|
|
+ # the fd from epoll(7) anymore, causing a 100% CPU poll loop.
|
|
|
+ fd = proc._sentinel_poll = os.dup(proc._popen.sentinel)
|
|
|
+ hub.add_reader(fd, self._event_process_exit, hub, proc)
|
|
|
+
|
|
|
+ def _untrack_child_process(self, proc, hub):
|
|
|
+ if proc._sentinel_poll is not None:
|
|
|
+ fd, proc._sentinel_poll = proc._sentinel_poll, None
|
|
|
+ hub.remove(fd)
|
|
|
+ os.close(fd)
|
|
|
+
|
|
|
def register_with_event_loop(self, hub):
|
|
|
"""Registers the async pool with the current event loop."""
|
|
|
self._result_handler.register_with_event_loop(hub)
|
|
@@ -440,8 +457,7 @@ class AsynPool(_pool.Pool):
|
|
|
self._create_write_handlers(hub)
|
|
|
|
|
|
# Add handler for when a process exits (calls maintain_pool)
|
|
|
- [hub.add_reader(fd, self._event_process_exit, hub, fd)
|
|
|
- for fd in self.process_sentinels]
|
|
|
+ [self._track_child_process(w, hub) for w in self._pool]
|
|
|
# Handle_result_event is called whenever one of the
|
|
|
# result queues are readable.
|
|
|
[hub.add_reader(fd, self.handle_result_event, fd)
|
|
@@ -528,7 +544,6 @@ class AsynPool(_pool.Pool):
|
|
|
fileno_to_outq = self._fileno_to_outq
|
|
|
fileno_to_synq = self._fileno_to_synq
|
|
|
busy_workers = self._busy_workers
|
|
|
- event_process_exit = self._event_process_exit
|
|
|
handle_result_event = self.handle_result_event
|
|
|
process_flush_queues = self.process_flush_queues
|
|
|
waiting_to_start = self._waiting_to_start
|
|
@@ -554,10 +569,9 @@ class AsynPool(_pool.Pool):
|
|
|
if job._scheduled_for and job._scheduled_for.inqW_fd == infd:
|
|
|
job._scheduled_for = proc
|
|
|
fileno_to_outq[proc.outqR_fd] = proc
|
|
|
+
|
|
|
# maintain_pool is called whenever a process exits.
|
|
|
- add_reader(
|
|
|
- proc.sentinel, event_process_exit, hub, proc.sentinel,
|
|
|
- )
|
|
|
+ self._track_child_process(proc, hub)
|
|
|
|
|
|
assert not isblocking(proc.outq._reader)
|
|
|
|
|
@@ -611,16 +625,16 @@ class AsynPool(_pool.Pool):
|
|
|
)
|
|
|
if inq:
|
|
|
busy_workers.discard(inq)
|
|
|
- remove_reader(proc.sentinel)
|
|
|
+ self._untrack_child_process(proc, hub)
|
|
|
waiting_to_start.discard(proc)
|
|
|
self._active_writes.discard(proc.inqW_fd)
|
|
|
- remove_writer(proc.inqW_fd)
|
|
|
- remove_reader(proc.outqR_fd)
|
|
|
+ remove_writer(proc.inq._writer)
|
|
|
+ remove_reader(proc.outq._reader)
|
|
|
if proc.synqR_fd:
|
|
|
- remove_reader(proc.synqR_fd)
|
|
|
+ remove_reader(proc.synq._reader)
|
|
|
if proc.synqW_fd:
|
|
|
self._active_writes.discard(proc.synqW_fd)
|
|
|
- remove_reader(proc.synqW_fd)
|
|
|
+ remove_reader(proc.synq._writer)
|
|
|
self.on_process_down = on_process_down
|
|
|
|
|
|
def _create_write_handlers(self, hub,
|