commit:     0903a300c9d79900389e59eab6fcfe9f12dbab51
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Oct  8 04:52:08 2014 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Sun Oct 19 17:29:28 2014 +0000
URL:        
http://sources.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=0903a300

bin/ebuild-ipc.py: nonblocking fifo write

Use nonblocking write instead of a fork for writing to the fifo.
This has the advantage of avoiding a possible python futex deadlock
following fork as reported in bug #524328.

X-Gentoo-Bug: 524328
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=524328

---
 bin/ebuild-ipc.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 56 insertions(+), 8 deletions(-)

diff --git a/bin/ebuild-ipc.py b/bin/ebuild-ipc.py
index 3e6d90c..b47ee23 100755
--- a/bin/ebuild-ipc.py
+++ b/bin/ebuild-ipc.py
@@ -5,6 +5,7 @@
 # This is a helper which ebuild processes can use
 # to communicate with portage's main python process.
 
+import errno
 import logging
 import os
 import pickle
@@ -44,19 +45,66 @@ import portage
 portage._internal_caller = True
 portage._disable_legacy_globals()
 
-from portage.util._async.ForkProcess import ForkProcess
 from portage.util._eventloop.global_event_loop import global_event_loop
+from _emerge.AbstractPollTask import AbstractPollTask
 from _emerge.PipeReader import PipeReader
 
-class FifoWriter(ForkProcess):
+RETURNCODE_WRITE_FAILED = 2
 
-       __slots__ = ('buf', 'fifo',)
+class FifoWriter(AbstractPollTask):
 
-       def _run(self):
-               # Atomically write the whole buffer into the fifo.
-               with open(self.fifo, 'wb', 0) as f:
-                       f.write(self.buf)
-               return os.EX_OK
+       __slots__ = ('buf', 'fifo', '_fd', '_reg_id',)
+
+       def _start(self):
+               try:
+                       self._fd = os.open(self.fifo, os.O_WRONLY|os.O_NONBLOCK)
+               except OSError as e:
+                       if e.errno == errno.ENXIO:
+                               # This happens if the daemon has been killed.
+                               self.returncode = RETURNCODE_WRITE_FAILED
+                               self._unregister()
+                               self._async_wait()
+                               return
+                       else:
+                               raise
+               self._reg_id = self.scheduler.io_add_watch(
+                       self._fd,
+                       self.scheduler.IO_OUT | self.scheduler.IO_HUP | \
+                       self._exceptional_events, self._output_handler)
+               self._registered = True
+
+       def _output_handler(self, fd, event):
+               if event & self.scheduler.IO_OUT:
+                       # The whole buf should be able to fit in the fifo with
+                       # a single write call, so there's no valid reason for
+                       # os.write to raise EAGAIN here.
+                       buf = self.buf
+                       while buf:
+                               buf = buf[os.write(fd, buf):]
+                       self.returncode = os.EX_OK
+                       self._unregister()
+                       self.wait()
+                       return False
+               else:
+                       self._unregister_if_appropriate(event)
+                       if not self._registered:
+                               self.returncode = RETURNCODE_WRITE_FAILED
+                               self.wait()
+                               return False
+               return True
+
+       def _cancel(self):
+               self.returncode = self._cancelled_returncode
+               self._unregister()
+
+       def _unregister(self):
+               self._registered = False
+               if self._reg_id is not None:
+                       self.scheduler.source_remove(self._reg_id)
+                       self._reg_id = None
+               if self._fd is not None:
+                       os.close(self._fd)
+                       self._fd = None
 
 class EbuildIpc(object):
 

Reply via email to