Refactor interaction between EbuildIpcDaemon and ExitCommand.
authorZac Medico <zmedico@gentoo.org>
Fri, 13 Aug 2010 06:11:53 +0000 (23:11 -0700)
committerZac Medico <zmedico@gentoo.org>
Fri, 13 Aug 2010 06:11:53 +0000 (23:11 -0700)
pym/_emerge/EbuildIpcDaemon.py
pym/portage/tests/ebuild/test_ipc_daemon.py

index 2f4d8933cda5ca638f75dc1617c3359acd7916c6..0715dc1f0f8e07b50ffd8dfb64c8aaeb33b7a5ef 100644 (file)
@@ -2,6 +2,7 @@
 # Distributed under the terms of the GNU General Public License v2
 
 import array
+import errno
 import pickle
 from portage import os
 from _emerge.FifoIpcDaemon import FifoIpcDaemon
@@ -40,7 +41,26 @@ class EbuildIpcDaemon(FifoIpcDaemon):
                                obj = pickle.loads(buf.tostring())
                                cmd_key = obj[0]
                                cmd_handler = self.commands[cmd_key]
-                               cmd_handler(obj, self._send_reply)
+                               reply = cmd_handler(obj)
+                               try:
+                                       self._send_reply(reply)
+                               except OSError as e:
+                                       if e.errno == errno.ENXIO:
+                                               # This happens if the client side has been killed.
+                                               pass
+                                       else:
+                                               raise
+
+                               # Allow the command to execute hooks after its reply
+                               # has been sent. This hook is used by the 'exit'
+                               # command to kill the ebuild process. For some
+                               # reason, the ebuild-ipc helper hangs up the
+                               # ebuild process if it is waiting for a reply
+                               # when we try to kill the ebuild process.
+                               reply_hook = getattr(cmd_handler,
+                                       'reply_hook', None)
+                               if reply_hook is not None:
+                                       reply_hook()
 
                self._unregister_if_appropriate(event)
                return self._registered
index bd27a38d14b944632cfbeee1363930a1db237601..48412a1c3628f4373cb0e1776e8eff0d9aafbaa5 100644 (file)
@@ -15,24 +15,22 @@ from _emerge.TaskScheduler import TaskScheduler
 class ExitCommand(object):
 
        def __init__(self):
-               self.callback = None
+               self.reply_hook = None
                self.exitcode = None
 
-       def __call__(self, argv, send_reply):
-               duplicate = False
+       def __call__(self, argv):
+
                if self.exitcode is not None:
                        # Ignore all but the first call, since if die is called
                        # then we certainly want to honor that exitcode, even
                        # the ebuild process manages to send a second exit
                        # command.
-                       duplicate = True
+                       self.reply_hook = None
                else:
                        self.exitcode = int(argv[1])
 
                # (stdout, stderr, returncode)
-               send_reply(('', '', 0))
-               if not duplicate and self.callback is not None:
-                       self.callback()
+               return ('', '', 0)
 
 class IpcDaemonTestCase(TestCase):
 
@@ -62,7 +60,7 @@ class IpcDaemonTestCase(TestCase):
                                def exit_command_callback():
                                        daemon.cancel()
                                        proc.cancel()
-                               exit_command.callback = exit_command_callback
+                               exit_command.reply_hook = exit_command_callback
                                task_scheduler.add(daemon)
                                task_scheduler.add(proc)
                                task_scheduler.run()