Bug #233458 - Fix AsynchronousTask exit listener handling so that an exit
authorZac Medico <zmedico@gentoo.org>
Thu, 31 Jul 2008 10:37:43 +0000 (10:37 -0000)
committerZac Medico <zmedico@gentoo.org>
Thu, 31 Jul 2008 10:37:43 +0000 (10:37 -0000)
listener will never get called after it's been passed into
removeExitListener(), since the caller of removeExitListener() needs to
be able to be able to trust that the given exit listener will not be
called under any circumstances.

svn path=/main/trunk/; revision=11297

pym/_emerge/__init__.py

index e51f0c12e51223972cbd3c30f145074d74bfffa9..c99dfb09a22f0404e3c5f84a37d1d83380a3f6db 100644 (file)
@@ -1599,7 +1599,7 @@ class AsynchronousTask(SlotObject):
        """
 
        __slots__ = ("background", "cancelled", "returncode") + \
-               ("_exit_listeners", "_start_listeners")
+               ("_exit_listeners", "_exit_listener_stack", "_start_listeners")
 
        def start(self):
                """
@@ -1665,6 +1665,8 @@ class AsynchronousTask(SlotObject):
 
        def removeExitListener(self, f):
                if self._exit_listeners is None:
+                       if self._exit_listener_stack is not None:
+                               self._exit_listener_stack.remove(f)
                        return
                self._exit_listeners.remove(f)
 
@@ -1680,12 +1682,22 @@ class AsynchronousTask(SlotObject):
 
                        # This prevents recursion, in case one of the
                        # exit handlers triggers this method again by
-                       # calling wait().
-                       exit_listeners = self._exit_listeners
+                       # calling wait(). Use a stack that gives
+                       # removeExitListener() an opportunity to consume
+                       # listeners from the stack, before they can get
+                       # called below. This is necessary because a call
+                       # to one exit listener may result in a call to
+                       # removeExitListener() for another listener on
+                       # the stack. That listener needs to be removed
+                       # from the stack since it would be inconsistent
+                       # to call it after it has been been passed into
+                       # removeExitListener().
+                       self._exit_listener_stack = self._exit_listeners
                        self._exit_listeners = None
 
-                       for f in exit_listeners:
-                               f(self)
+                       self._exit_listener_stack.reverse()
+                       while self._exit_listener_stack:
+                               self._exit_listener_stack.pop()(self)
 
 class PipeReader(AsynchronousTask):