summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2012-02-16 22:03:30 -0800
committerZac Medico <zmedico@gentoo.org>2012-02-16 22:03:30 -0800
commited9125487af39181bf3a00ba46a2bda7ea1d68d2 (patch)
tree01bda38bb56888a27503987e73a539e0e7731fc3
parentEventLoop: wakeup poll loop to receive sigchild (diff)
downloadportage-ed9125487af39181bf3a00ba46a2bda7ea1d68d2.tar.gz
portage-ed9125487af39181bf3a00ba46a2bda7ea1d68d2.tar.bz2
portage-ed9125487af39181bf3a00ba46a2bda7ea1d68d2.zip
EventLoop.child_watch_add: dynamic IO watch
The IO watch is dynamically registered and unregistered as needed, since we don't want to consider it as a valid source of events when there are no child listeners. It's important to distinguish when there are no valid sources of IO events, in order to avoid an endless poll call if there's no timeout. This fixes possbible endless poll calls since commit 1979a6cdfcd8c6bae4565982d82d862be07ba5be.
-rw-r--r--pym/portage/util/_eventloop/EventLoop.py32
1 files changed, 22 insertions, 10 deletions
diff --git a/pym/portage/util/_eventloop/EventLoop.py b/pym/portage/util/_eventloop/EventLoop.py
index e38134258..7f171fb8c 100644
--- a/pym/portage/util/_eventloop/EventLoop.py
+++ b/pym/portage/util/_eventloop/EventLoop.py
@@ -57,6 +57,7 @@ class EventLoop(object):
self._child_handlers = {}
self._sigchld_read = None
self._sigchld_write = None
+ self._sigchld_src_id = None
self._pid = os.getpid()
def _poll(self, timeout=None):
@@ -246,19 +247,26 @@ class EventLoop(object):
source_id = self._event_handler_id
self._child_handlers[source_id] = self._child_callback_class(
callback=callback, data=data, pid=pid, source_id=source_id)
+
if self._sigchld_read is None:
- self._sigchld_init()
+ self._sigchld_read, self._sigchld_write = os.pipe()
+ fcntl.fcntl(self._sigchld_read, fcntl.F_SETFL,
+ fcntl.fcntl(self._sigchld_read, fcntl.F_GETFL) | os.O_NONBLOCK)
+
+ # The IO watch is dynamically registered and unregistered as
+ # needed, since we don't want to consider it as a valid source
+ # of events when there are no child listeners. It's important
+ # to distinguish when there are no valid sources of IO events,
+ # in order to avoid an endless poll call if there's no timeout.
+ if self._sigchld_src_id is None:
+ self._sigchld_src_id = self.io_add_watch(
+ self._sigchld_read, self.IO_IN, self._sigchld_io_cb)
+ signal.signal(signal.SIGCHLD, self._sigchld_sig_cb)
+
# poll now, in case the SIGCHLD has already arrived
self._poll_child_processes()
return source_id
- def _sigchld_init(self):
- self._sigchld_read, self._sigchld_write = os.pipe()
- fcntl.fcntl(self._sigchld_read, fcntl.F_SETFL,
- fcntl.fcntl(self._sigchld_read, fcntl.F_GETFL) | os.O_NONBLOCK)
- self.io_add_watch(self._sigchld_read, self.IO_IN, self._sigchld_io_cb)
- signal.signal(signal.SIGCHLD, self._sigchld_sig_cb)
-
def _sigchld_sig_cb(self, signum, frame):
# If this signal handler was not installed by the
# current process then the signal doesn't belong to
@@ -292,14 +300,14 @@ class EventLoop(object):
if e.errno != errno.ECHILD:
raise
del e
- self._child_handlers.pop(x.source_id, None)
+ self.source_remove(x.source_id)
else:
# With waitpid and WNOHANG, only check the
# first element of the tuple since the second
# element may vary (bug #337465).
if wait_retval[0] != 0:
calls += 1
- self._child_handlers.pop(x.source_id, None)
+ self.source_remove(x.source_id)
x.callback(x.pid, wait_retval[1], x.data)
return bool(calls)
@@ -429,6 +437,10 @@ class EventLoop(object):
"""
x = self._child_handlers.pop(reg_id, None)
if x is not None:
+ if not self._child_handlers:
+ signal.signal(signal.SIGCHLD, signal.SIG_DFL)
+ self.source_remove(self._sigchld_src_id)
+ self._sigchld_src_id = None
return True
idle_callback = self._idle_callbacks.pop(reg_id, None)
if idle_callback is not None: