EbuildIpcDaemon: use non-blocking write
authorZac Medico <zmedico@gentoo.org>
Thu, 17 Mar 2011 21:39:53 +0000 (14:39 -0700)
committerZac Medico <zmedico@gentoo.org>
Thu, 17 Mar 2011 21:39:53 +0000 (14:39 -0700)
This prevents the Scheduler from hanging if the client is killed before
we can send the reply, as reported by David James:

  http://codereview.chromium.org/6713003

This commit depends on ebuild-ipc (the client) opening the other side
of this fifo before it sends its request, which has already been added
in commit eff879ff0ce7dcc1ce68d5f16de1ec73051f8c18.

pym/_emerge/EbuildIpcDaemon.py

index efcda93169ebb5d9161f7f2111e88d7f35a55b6f..5dabe34b35f9b47edbf810cff79abbe7160a8c7b 100644 (file)
@@ -1,9 +1,12 @@
-# Copyright 2010 Gentoo Foundation
+# Copyright 2010-2011 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 import errno
+import logging
 import pickle
 from portage import os
+from portage.localization import _
+from portage.util import writemsg_level
 from _emerge.FifoIpcDaemon import FifoIpcDaemon
 from _emerge.PollConstants import PollConstants
 
@@ -83,7 +86,23 @@ class EbuildIpcDaemon(FifoIpcDaemon):
 
        def _send_reply(self, reply):
                # File streams are in unbuffered mode since we do atomic
-               # read and write of whole pickles.
-               output_file = open(self.output_fifo, 'wb', 0)
-               output_file.write(pickle.dumps(reply))
-               output_file.close()
+               # read and write of whole pickles. Use non-blocking mode so
+               # we don't hang if the client is killed before we can send
+               # the reply. We rely on the client opening the other side
+               # of this fifo before it sends its request, since otherwise
+               # we'd have a race condition with this open call raising
+               # ENXIO if the client hasn't opened the fifo yet.
+               try:
+                       output_fd = os.open(self.output_fifo,
+                               os.O_WRONLY | os.O_NONBLOCK)
+                       try:
+                               os.write(output_fd, pickle.dumps(reply))
+                       finally:
+                               os.close(output_fd)
+               except OSError as e:
+                       # This probably means that the client has been killed,
+                       # which causes open to fail with ENXIO.
+                       writemsg_level(
+                               "!!! EbuildIpcDaemon %s: %s\n" % \
+                               (_('failed to send reply'), e),
+                               level=logging.ERROR, noiselevel=-1)