1   
 2   
 3   
 4   
 5   
 6   
 7   
 8   
 9   
10   
11   
12   
13   
14   
15   
16  """ 
17  Miscellaneous utilities; these should be imported from C{buildbot.util}, not 
18  directly from this module. 
19  """ 
20   
21  from twisted.python import log 
22  from twisted.internet import defer 
23   
25      """ 
26      Wrap a function which returns a Deferred with a DeferredLock.  The 
27      DeferredLock is given by the argument to the decorator; if this argument is 
28      a string, then the function is assumed to be a method, and the named 
29      attribute of SELF is used as the lock object. 
30      """ 
31      def decorator(fn): 
32          def wrapper(*args, **kwargs): 
33              lock = lock_or_attr 
34              if isinstance(lock, basestring): 
35                  lock = getattr(args[0], lock) 
36              d = lock.acquire() 
37              d.addCallback(lambda _ : fn(*args, **kwargs)) 
38              def release(val): 
39                  lock.release() 
40                  return val 
 41              d.addBoth(release) 
42              return d 
43          return wrapper 
44      return decorator 
45   
47      """ 
48      A method wrapper to serialize calls to a deferred method.  If a second call 
49      occurs while the first call is still executing, it will not begin until the 
50      first call has finished.  If multiple calls queue up, they will be 
51      collapsed into a single call. 
52   
53      The effect is that the underlying method is guaranteed to be called at 
54      least once after every call to the wrapper. 
55   
56      Note that this cannot be used as a decorator on a method, as it will 
57      serialize invocations across all class instances.  Tests can monkey-patch 
58      the C{_quiet} method to be notified when all planned invocations are 
59      complete. 
60      """ 
62          self.method = method 
63          self.running = False 
64          self.pending_deferreds = [] 
 65   
67          d = defer.Deferred() 
68          self.pending_deferreds.append(d) 
69          if not self.running: 
70              self.start() 
71          return d 
 72   
74          self.running = True 
75          invocation_deferreds = self.pending_deferreds 
76          self.pending_deferreds = [] 
77          d = self.method() 
78          d.addErrback(log.err, 'in invocation of %r' % (self.method,)) 
79   
80          def notify_callers(_): 
81              for d in invocation_deferreds: 
82                  d.callback(None) 
 83          d.addCallback(notify_callers) 
84   
85          def next(_): 
86              self.running = False 
87              if self.pending_deferreds: 
88                  self.start() 
89              else: 
90                  self._quiet() 
 91          d.addBoth(next) 
92   
94          pass 
95