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