1 |
Author: zmedico |
2 |
Date: 2008-07-31 10:37:43 +0000 (Thu, 31 Jul 2008) |
3 |
New Revision: 11297 |
4 |
|
5 |
Modified: |
6 |
main/trunk/pym/_emerge/__init__.py |
7 |
Log: |
8 |
Bug #233458 - Fix AsynchronousTask exit listener handling so that an exit |
9 |
listener will never get called after it's been passed into |
10 |
removeExitListener(), since the caller of removeExitListener() needs to |
11 |
be able to be able to trust that the given exit listener will not be |
12 |
called under any circumstances. |
13 |
|
14 |
|
15 |
Modified: main/trunk/pym/_emerge/__init__.py |
16 |
=================================================================== |
17 |
--- main/trunk/pym/_emerge/__init__.py 2008-07-31 07:25:35 UTC (rev 11296) |
18 |
+++ main/trunk/pym/_emerge/__init__.py 2008-07-31 10:37:43 UTC (rev 11297) |
19 |
@@ -1599,7 +1599,7 @@ |
20 |
""" |
21 |
|
22 |
__slots__ = ("background", "cancelled", "returncode") + \ |
23 |
- ("_exit_listeners", "_start_listeners") |
24 |
+ ("_exit_listeners", "_exit_listener_stack", "_start_listeners") |
25 |
|
26 |
def start(self): |
27 |
""" |
28 |
@@ -1665,6 +1665,8 @@ |
29 |
|
30 |
def removeExitListener(self, f): |
31 |
if self._exit_listeners is None: |
32 |
+ if self._exit_listener_stack is not None: |
33 |
+ self._exit_listener_stack.remove(f) |
34 |
return |
35 |
self._exit_listeners.remove(f) |
36 |
|
37 |
@@ -1680,12 +1682,22 @@ |
38 |
|
39 |
# This prevents recursion, in case one of the |
40 |
# exit handlers triggers this method again by |
41 |
- # calling wait(). |
42 |
- exit_listeners = self._exit_listeners |
43 |
+ # calling wait(). Use a stack that gives |
44 |
+ # removeExitListener() an opportunity to consume |
45 |
+ # listeners from the stack, before they can get |
46 |
+ # called below. This is necessary because a call |
47 |
+ # to one exit listener may result in a call to |
48 |
+ # removeExitListener() for another listener on |
49 |
+ # the stack. That listener needs to be removed |
50 |
+ # from the stack since it would be inconsistent |
51 |
+ # to call it after it has been been passed into |
52 |
+ # removeExitListener(). |
53 |
+ self._exit_listener_stack = self._exit_listeners |
54 |
self._exit_listeners = None |
55 |
|
56 |
- for f in exit_listeners: |
57 |
- f(self) |
58 |
+ self._exit_listener_stack.reverse() |
59 |
+ while self._exit_listener_stack: |
60 |
+ self._exit_listener_stack.pop()(self) |
61 |
|
62 |
class PipeReader(AsynchronousTask): |