Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: third_party/buildbot_7_12/buildbot/locks.py

Issue 12207158: Bye bye buildbot 0.7.12. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 # -*- test-case-name: buildbot.test.test_locks -*-
2
3 from twisted.python import log
4 from twisted.internet import reactor, defer
5 from buildbot import util
6
7 if False: # for debugging
8 debuglog = log.msg
9 else:
10 debuglog = lambda m: None
11
12 class BaseLock:
13 """
14 Class handling claiming and releasing of L{self}, and keeping track of
15 current and waiting owners.
16
17 @note: Ideally, we'd like to maintain FIFO order. The place to do that
18 would be the L{isAvailable()} function. However, this function is
19 called by builds/steps both for the first time, and after waking
20 them up by L{self} from the L{self.waiting} queue. There is
21 currently no way of distinguishing between them.
22 """
23 description = "<BaseLock>"
24
25 def __init__(self, name, maxCount=1):
26 self.name = name # Name of the lock
27 self.waiting = [] # Current queue, tuples (LockAccess, deferred)
28 self.owners = [] # Current owners, tuples (owner, LockAccess)
29 self.maxCount=maxCount # maximal number of counting owners
30
31 def __repr__(self):
32 return self.description
33
34 def _getOwnersCount(self):
35 """ Return the number of current exclusive and counting owners.
36
37 @return: Tuple (number exclusive owners, number counting owners)
38 """
39 num_excl, num_counting = 0, 0
40 for owner in self.owners:
41 if owner[1].mode == 'exclusive':
42 num_excl = num_excl + 1
43 else: # mode == 'counting'
44 num_counting = num_counting + 1
45
46 assert (num_excl == 1 and num_counting == 0) \
47 or (num_excl == 0 and num_counting <= self.maxCount)
48 return num_excl, num_counting
49
50
51 def isAvailable(self, access):
52 """ Return a boolean whether the lock is available for claiming """
53 debuglog("%s isAvailable(%s): self.owners=%r"
54 % (self, access, self.owners))
55 num_excl, num_counting = self._getOwnersCount()
56 if access.mode == 'counting':
57 # Wants counting access
58 return num_excl == 0 and num_counting < self.maxCount
59 else:
60 # Wants exclusive access
61 return num_excl == 0 and num_counting == 0
62
63 def claim(self, owner, access):
64 """ Claim the lock (lock must be available) """
65 debuglog("%s claim(%s, %s)" % (self, owner, access.mode))
66 assert owner is not None
67 assert self.isAvailable(access), "ask for isAvailable() first"
68
69 assert isinstance(access, LockAccess)
70 assert access.mode in ['counting', 'exclusive']
71 self.owners.append((owner, access))
72 debuglog(" %s is claimed '%s'" % (self, access.mode))
73
74 def release(self, owner, access):
75 """ Release the lock """
76 assert isinstance(access, LockAccess)
77
78 debuglog("%s release(%s, %s)" % (self, owner, access.mode))
79 entry = (owner, access)
80 assert entry in self.owners
81 self.owners.remove(entry)
82 # who can we wake up?
83 # After an exclusive access, we may need to wake up several waiting.
84 # Break out of the loop when the first waiting client should not be awak ened.
85 num_excl, num_counting = self._getOwnersCount()
86 while len(self.waiting) > 0:
87 access, d = self.waiting[0]
88 if access.mode == 'counting':
89 if num_excl > 0 or num_counting == self.maxCount:
90 break
91 else:
92 num_counting = num_counting + 1
93 else:
94 # access.mode == 'exclusive'
95 if num_excl > 0 or num_counting > 0:
96 break
97 else:
98 num_excl = num_excl + 1
99
100 del self.waiting[0]
101 reactor.callLater(0, d.callback, self)
102
103 def waitUntilMaybeAvailable(self, owner, access):
104 """Fire when the lock *might* be available. The caller will need to
105 check with isAvailable() when the deferred fires. This loose form is
106 used to avoid deadlocks. If we were interested in a stronger form,
107 this would be named 'waitUntilAvailable', and the deferred would fire
108 after the lock had been claimed.
109 """
110 debuglog("%s waitUntilAvailable(%s)" % (self, owner))
111 assert isinstance(access, LockAccess)
112 if self.isAvailable(access):
113 return defer.succeed(self)
114 d = defer.Deferred()
115 self.waiting.append((access, d))
116 return d
117
118
119 class RealMasterLock(BaseLock):
120 def __init__(self, lockid):
121 BaseLock.__init__(self, lockid.name, lockid.maxCount)
122 self.description = "<MasterLock(%s, %s)>" % (self.name, self.maxCount)
123
124 def getLock(self, slave):
125 return self
126
127 class RealSlaveLock:
128 def __init__(self, lockid):
129 self.name = lockid.name
130 self.maxCount = lockid.maxCount
131 self.maxCountForSlave = lockid.maxCountForSlave
132 self.description = "<SlaveLock(%s, %s, %s)>" % (self.name,
133 self.maxCount,
134 self.maxCountForSlave)
135 self.locks = {}
136
137 def __repr__(self):
138 return self.description
139
140 def getLock(self, slavebuilder):
141 slavename = slavebuilder.slave.slavename
142 if not self.locks.has_key(slavename):
143 maxCount = self.maxCountForSlave.get(slavename,
144 self.maxCount)
145 lock = self.locks[slavename] = BaseLock(self.name, maxCount)
146 desc = "<SlaveLock(%s, %s)[%s] %d>" % (self.name, maxCount,
147 slavename, id(lock))
148 lock.description = desc
149 self.locks[slavename] = lock
150 return self.locks[slavename]
151
152
153 class LockAccess(util.ComparableMixin):
154 """ I am an object representing a way to access a lock.
155
156 @param lockid: LockId instance that should be accessed.
157 @type lockid: A MasterLock or SlaveLock instance.
158
159 @param mode: Mode of accessing the lock.
160 @type mode: A string, either 'counting' or 'exclusive'.
161 """
162
163 compare_attrs = ['lockid', 'mode']
164 def __init__(self, lockid, mode):
165 self.lockid = lockid
166 self.mode = mode
167
168 assert isinstance(lockid, (MasterLock, SlaveLock))
169 assert mode in ['counting', 'exclusive']
170
171
172 class BaseLockId(util.ComparableMixin):
173 """ Abstract base class for LockId classes.
174
175 Sets up the 'access()' function for the LockId's available to the user
176 (MasterLock and SlaveLock classes).
177 Derived classes should add
178 - Comparison with the L{util.ComparableMixin} via the L{compare_attrs}
179 class variable.
180 - Link to the actual lock class should be added with the L{lockClass}
181 class variable.
182 """
183 def access(self, mode):
184 """ Express how the lock should be accessed """
185 assert mode in ['counting', 'exclusive']
186 return LockAccess(self, mode)
187
188 def defaultAccess(self):
189 """ For buildbot 0.7.7 compability: When user doesn't specify an access
190 mode, this one is chosen.
191 """
192 return self.access('counting')
193
194
195
196 # master.cfg should only reference the following MasterLock and SlaveLock
197 # classes. They are identifiers that will be turned into real Locks later,
198 # via the BotMaster.getLockByID method.
199
200 class MasterLock(BaseLockId):
201 """I am a semaphore that limits the number of simultaneous actions.
202
203 Builds and BuildSteps can declare that they wish to claim me as they run.
204 Only a limited number of such builds or steps will be able to run
205 simultaneously. By default this number is one, but my maxCount parameter
206 can be raised to allow two or three or more operations to happen at the
207 same time.
208
209 Use this to protect a resource that is shared among all builders and all
210 slaves, for example to limit the load on a common SVN repository.
211 """
212
213 compare_attrs = ['name', 'maxCount']
214 lockClass = RealMasterLock
215 def __init__(self, name, maxCount=1):
216 self.name = name
217 self.maxCount = maxCount
218
219 class SlaveLock(BaseLockId):
220 """I am a semaphore that limits simultaneous actions on each buildslave.
221
222 Builds and BuildSteps can declare that they wish to claim me as they run.
223 Only a limited number of such builds or steps will be able to run
224 simultaneously on any given buildslave. By default this number is one,
225 but my maxCount parameter can be raised to allow two or three or more
226 operations to happen on a single buildslave at the same time.
227
228 Use this to protect a resource that is shared among all the builds taking
229 place on each slave, for example to limit CPU or memory load on an
230 underpowered machine.
231
232 Each buildslave will get an independent copy of this semaphore. By
233 default each copy will use the same owner count (set with maxCount), but
234 you can provide maxCountForSlave with a dictionary that maps slavename to
235 owner count, to allow some slaves more parallelism than others.
236
237 """
238
239 compare_attrs = ['name', 'maxCount', '_maxCountForSlaveList']
240 lockClass = RealSlaveLock
241 def __init__(self, name, maxCount=1, maxCountForSlave={}):
242 self.name = name
243 self.maxCount = maxCount
244 self.maxCountForSlave = maxCountForSlave
245 # for comparison purposes, turn this dictionary into a stably-sorted
246 # list of tuples
247 self._maxCountForSlaveList = self.maxCountForSlave.items()
248 self._maxCountForSlaveList.sort()
249 self._maxCountForSlaveList = tuple(self._maxCountForSlaveList)
OLDNEW
« no previous file with comments | « third_party/buildbot_7_12/buildbot/interfaces.py ('k') | third_party/buildbot_7_12/buildbot/manhole.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698