1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
5 from _emerge.SlotObject import SlotObject
6 class AsynchronousTask(SlotObject):
8 Subclasses override _wait() and _poll() so that calls
9 to public methods can be wrapped for implementing
10 hooks such as exit listener notification.
12 Sublasses should call self.wait() to notify exit listeners after
13 the task is complete and self.returncode has been set.
16 __slots__ = ("background", "cancelled", "returncode") + \
17 ("_exit_listeners", "_exit_listener_stack", "_start_listeners")
21 Start an asynchronous task and then return as soon as possible.
27 self.returncode = os.EX_OK
31 return self.returncode is None
35 if self.returncode is not None:
36 return self.returncode
40 return self.returncode
43 if self.returncode is None:
46 return self.returncode
49 return self.returncode
52 if not self.cancelled:
59 Subclasses should implement this, as a template method
60 to be called by AsynchronousTask.cancel().
64 def addStartListener(self, f):
66 The function will be called with one argument, a reference to self.
68 if self._start_listeners is None:
69 self._start_listeners = []
70 self._start_listeners.append(f)
72 def removeStartListener(self, f):
73 if self._start_listeners is None:
75 self._start_listeners.remove(f)
77 def _start_hook(self):
78 if self._start_listeners is not None:
79 start_listeners = self._start_listeners
80 self._start_listeners = None
82 for f in start_listeners:
85 def addExitListener(self, f):
87 The function will be called with one argument, a reference to self.
89 if self._exit_listeners is None:
90 self._exit_listeners = []
91 self._exit_listeners.append(f)
93 def removeExitListener(self, f):
94 if self._exit_listeners is None:
95 if self._exit_listener_stack is not None:
96 self._exit_listener_stack.remove(f)
98 self._exit_listeners.remove(f)
100 def _wait_hook(self):
102 Call this method after the task completes, just before returning
103 the returncode from wait() or poll(). This hook is
104 used to trigger exit listeners when the returncode first
107 if self.returncode is not None and \
108 self._exit_listeners is not None:
110 # This prevents recursion, in case one of the
111 # exit handlers triggers this method again by
112 # calling wait(). Use a stack that gives
113 # removeExitListener() an opportunity to consume
114 # listeners from the stack, before they can get
115 # called below. This is necessary because a call
116 # to one exit listener may result in a call to
117 # removeExitListener() for another listener on
118 # the stack. That listener needs to be removed
119 # from the stack since it would be inconsistent
120 # to call it after it has been been passed into
121 # removeExitListener().
122 self._exit_listener_stack = self._exit_listeners
123 self._exit_listeners = None
125 self._exit_listener_stack.reverse()
126 while self._exit_listener_stack:
127 self._exit_listener_stack.pop()(self)