Ticket #313: lockaccess2.patch
| File lockaccess2.patch, 29.2 kB (added by albertHofkamp, 6 months ago) |
|---|
-
buildbot-0.7.7/buildbot/locks.py
old new 23 23 description = "<BaseLock>" 24 24 25 25 def __init__(self, name, maxCount=1): 26 self.name = name 27 self.waiting = [] 28 self.owners = [] 29 self.maxCount=maxCount 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 30 31 31 def __repr__(self): 32 32 return self.description 33 33 34 def isAvailable(self): 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): 35 52 """ Return a boolean whether the lock is available for claiming """ 36 debuglog("%s isAvailable: self.owners=%r" % (self, self.owners)) 37 return len(self.owners) < self.maxCount 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 38 62 39 def claim(self, owner ):63 def claim(self, owner, access): 40 64 """ Claim the lock (lock must be available) """ 41 debuglog("%s claim(%s )" % (self, owner))65 debuglog("%s claim(%s, %s)" % (self, owner, access.mode)) 42 66 assert owner is not None 43 assert len(self.owners) < self.maxCount, "ask for isAvailable() first" 44 self.owners.append(owner) 45 debuglog(" %s is claimed" % (self,)) 46 47 def release(self, owner): 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): 48 75 """ Release the lock """ 49 debuglog("%s release(%s)" % (self, owner)) 50 assert owner in self.owners 51 self.owners.remove(owner) 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) 52 82 # who can we wake up? 53 if self.waiting: 54 d = self.waiting.pop(0) 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 awakened. 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] 55 101 reactor.callLater(0, d.callback, self) 56 102 57 def waitUntilMaybeAvailable(self, owner ):103 def waitUntilMaybeAvailable(self, owner, access): 58 104 """Fire when the lock *might* be available. The caller will need to 59 105 check with isAvailable() when the deferred fires. This loose form is 60 106 used to avoid deadlocks. If we were interested in a stronger form, … … 62 108 after the lock had been claimed. 63 109 """ 64 110 debuglog("%s waitUntilAvailable(%s)" % (self, owner)) 65 if self.isAvailable(): 111 assert isinstance(access, LockAccess) 112 if self.isAvailable(access): 66 113 return defer.succeed(self) 67 114 d = defer.Deferred() 68 self.waiting.append( d)115 self.waiting.append((access, d)) 69 116 return d 70 117 71 118 … … 103 150 return self.locks[slavename] 104 151 105 152 153 class LockAccess: 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 def __init__(self, lockid, mode): 163 self.lockid = lockid 164 self.mode = mode 165 166 assert isinstance(lockid, (MasterLock, SlaveLock)) 167 assert mode in ['counting', 'exclusive'] 168 169 106 170 class BaseLockId(util.ComparableMixin): 107 171 """ Abstract base class for LockId classes. 108 172 173 Sets up the 'access()' function for the LockId's available to the user 174 (MasterLock and SlaveLock classes). 109 175 Derived classes should add 110 176 - Comparison with the L{util.ComparableMixin} via the L{compare_attrs} 111 177 class variable. 112 178 - Link to the actual lock class should be added with the L{lockClass} 113 179 class variable. 114 180 """ 181 def access(self, mode): 182 """ Express how the lock should be accessed """ 183 assert mode in ['counting', 'exclusive'] 184 return LockAccess(self, mode) 185 186 def defaultAccess(self): 187 """ For buildbot 0.7.7 compability: When user doesn't specify an access 188 mode, this one is chosen. 189 """ 190 return self.access('counting') 191 192 115 193 116 194 # master.cfg should only reference the following MasterLock and SlaveLock 117 195 # classes. They are identifiers that will be turned into real Locks later, -
buildbot-0.7.7/buildbot/master.py
old new 633 633 lock_dict = {} 634 634 for b in builders: 635 635 for l in b.get('locks', []): 636 if isinstance(l, locks.LockAccess): # User specified access to the lock 637 l = l.lockid 636 638 if lock_dict.has_key(l.name): 637 639 if lock_dict[l.name] is not l: 638 640 raise ValueError("Two different locks (%s and %s) " … … 645 647 # important. 646 648 for s in b['factory'].steps: 647 649 for l in s[1].get('locks', []): 650 if isinstance(l, locks.LockAccess): # User specified access to the lock 651 l = l.lockid 648 652 if lock_dict.has_key(l.name): 649 653 if lock_dict[l.name] is not l: 650 654 raise ValueError("Two different locks (%s and %s)" -
buildbot-0.7.7/buildbot/process/base.py
old new 7 7 from twisted.python.failure import Failure 8 8 from twisted.internet import reactor, defer, error 9 9 10 from buildbot import interfaces 10 from buildbot import interfaces, locks 11 11 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION 12 12 from buildbot.status.builder import Results, BuildRequestStatus 13 13 from buildbot.status.progress import BuildProgress … … 284 284 self.setupSlaveBuilder(slavebuilder) 285 285 286 286 # convert all locks into their real forms 287 self.locks = [self.builder.botmaster.getLockByID(l) 288 for l in self.locks] 287 lock_list = [] 288 for access in self.locks: 289 if not isinstance(access, locks.LockAccess): 290 # Buildbot 0.7.7 compability: user did not specify access 291 access = access.defaultAccess() 292 lock = self.builder.botmaster.getLockByID(access.lockid) 293 lock_list.append((lock, access)) 294 self.locks = lock_list 289 295 # then narrow SlaveLocks down to the right slave 290 self.locks = [l.getLock(self.slavebuilder) for l in self.locks] 296 self.locks = [(l.getLock(self.slavebuilder), la) 297 for l, la in self.locks] 291 298 self.remote = slavebuilder.remote 292 299 self.remote.notifyOnDisconnect(self.lostRemote) 293 300 d = self.deferred = defer.Deferred() … … 323 330 log.msg("acquireLocks(step %s, locks %s)" % (self, self.locks)) 324 331 if not self.locks: 325 332 return defer.succeed(None) 326 for lock in self.locks:327 if not lock.isAvailable( ):333 for lock, access in self.locks: 334 if not lock.isAvailable(access): 328 335 log.msg("Build %s waiting for lock %s" % (self, lock)) 329 d = lock.waitUntilMaybeAvailable(self )336 d = lock.waitUntilMaybeAvailable(self, access) 330 337 d.addCallback(self.acquireLocks) 331 338 return d 332 339 # all locks are available, claim them all 333 for lock in self.locks:334 lock.claim(self )340 for lock, access in self.locks: 341 lock.claim(self, access) 335 342 return defer.succeed(None) 336 343 337 344 def _startBuild_2(self, res): … … 555 562 556 563 def releaseLocks(self): 557 564 log.msg("releaseLocks(%s): %s" % (self, self.locks)) 558 for lock in self.locks:559 lock.release(self )565 for lock, access in self.locks: 566 lock.release(self, access) 560 567 561 568 # IBuildControl 562 569 -
buildbot-0.7.7/buildbot/process/buildstep.py
old new 8 8 from twisted.python.failure import Failure 9 9 from twisted.web.util import formatFailure 10 10 11 from buildbot import util 11 from buildbot import util, locks 12 12 from buildbot import interfaces 13 13 from buildbot.status import progress 14 14 from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, SKIPPED, \ … … 663 663 self.remote = remote 664 664 self.deferred = defer.Deferred() 665 665 # convert all locks into their real form 666 self.locks = [self.build.builder.botmaster.getLockByID(l) 667 for l in self.locks] 666 lock_list = [] 667 for access in self.locks: 668 if not isinstance(access, locks.LockAccess): 669 # Buildbot 0.7.7 compability: user did not specify access 670 access = access.defaultAccess() 671 lock = self.build.builder.botmaster.getLockByID(access.lockid) 672 lock_list.append((lock, access)) 673 self.locks = lock_list 668 674 # then narrow SlaveLocks down to the slave that this build is being 669 675 # run on 670 self.locks = [ l.getLock(self.build.slavebuilder) for lin self.locks]671 for l in self.locks:676 self.locks = [(l.getLock(self.build.slavebuilder), la) for l, la in self.locks] 677 for l, la in self.locks: 672 678 if l in self.build.locks: 673 679 log.msg("Hey, lock %s is claimed by both a Step (%s) and the" 674 680 " parent Build (%s)" % (l, self, self.build)) … … 681 687 log.msg("acquireLocks(step %s, locks %s)" % (self, self.locks)) 682 688 if not self.locks: 683 689 return defer.succeed(None) 684 for lock in self.locks:685 if not lock.isAvailable( ):690 for lock, access in self.locks: 691 if not lock.isAvailable(access): 686 692 log.msg("step %s waiting for lock %s" % (self, lock)) 687 d = lock.waitUntilMaybeAvailable(self )693 d = lock.waitUntilMaybeAvailable(self, access) 688 694 d.addCallback(self.acquireLocks) 689 695 return d 690 696 # all locks are available, claim them all 691 for lock in self.locks:692 lock.claim(self )697 for lock, access in self.locks: 698 lock.claim(self, access) 693 699 return defer.succeed(None) 694 700 695 701 def _startStep_2(self, res): … … 770 776 771 777 def releaseLocks(self): 772 778 log.msg("releaseLocks(%s): %s" % (self, self.locks)) 773 for lock in self.locks:774 lock.release(self )779 for lock, access in self.locks: 780 lock.release(self, access) 775 781 776 782 def finished(self, results): 777 783 if self.progress: -
buildbot-0.7.7/buildbot/test/test_locks.py
old new 12 12 from buildbot.test.runutils import RunMixin 13 13 from buildbot import locks 14 14 15 def claimHarder(lock, owner ):15 def claimHarder(lock, owner, la): 16 16 """Return a Deferred that will fire when the lock is claimed. Keep trying 17 17 until we succeed.""" 18 if lock.isAvailable( ):18 if lock.isAvailable(la): 19 19 #print "claimHarder(%s): claiming" % owner 20 lock.claim(owner )20 lock.claim(owner, la) 21 21 return defer.succeed(lock) 22 22 #print "claimHarder(%s): waiting" % owner 23 d = lock.waitUntilMaybeAvailable(owner )24 d.addCallback(claimHarder, owner )23 d = lock.waitUntilMaybeAvailable(owner, la) 24 d.addCallback(claimHarder, owner, la) 25 25 return d 26 26 27 def hold(lock, owner, mode="now"):27 def hold(lock, owner, la, mode="now"): 28 28 if mode == "now": 29 lock.release(owner )29 lock.release(owner, la) 30 30 elif mode == "very soon": 31 reactor.callLater(0, lock.release, owner )31 reactor.callLater(0, lock.release, owner, la) 32 32 elif mode == "soon": 33 reactor.callLater(0.1, lock.release, owner) 34 33 reactor.callLater(0.1, lock.release, owner, la) 35 34 36 35 class Unit(unittest.TestCase): 37 def testNow(self): 36 def testNowCounting(self): 37 lid = locks.MasterLock('dummy') 38 la = locks.LockAccess(lid, 'counting') 39 return self._testNow(la) 40 41 def testNowExclusive(self): 42 lid = locks.MasterLock('dummy') 43 la = locks.LockAccess(lid, 'exclusive') 44 return self._testNow(la) 45 46 def _testNow(self, la): 38 47 l = locks.BaseLock("name") 39 self.failUnless(l.isAvailable()) 40 l.claim("owner1") 41 self.failIf(l.isAvailable()) 42 l.release("owner1") 43 self.failUnless(l.isAvailable()) 44 45 def testLater(self): 46 lock = locks.BaseLock("name") 47 d = claimHarder(lock, "owner1") 48 d.addCallback(lambda lock: lock.release("owner1")) 49 return d 50 51 def testCompetition(self): 52 lock = locks.BaseLock("name") 53 d = claimHarder(lock, "owner1") 54 d.addCallback(self._claim1) 55 return d 56 def _claim1(self, lock): 48 self.failUnless(l.isAvailable(la)) 49 l.claim("owner1", la) 50 self.failIf(l.isAvailable(la)) 51 l.release("owner1", la) 52 self.failUnless(l.isAvailable(la)) 53 54 def testNowMixed1(self): 55 """ Test exclusive is not possible when a counting has the lock """ 56 lid = locks.MasterLock('dummy') 57 lac = locks.LockAccess(lid, 'counting') 58 lae = locks.LockAccess(lid, 'exclusive') 59 l = locks.BaseLock("name", maxCount=2) 60 self.failUnless(l.isAvailable(lac)) 61 l.claim("count-owner", lac) 62 self.failIf(l.isAvailable(lae)) 63 l.release("count-owner", lac) 64 self.failUnless(l.isAvailable(lac)) 65 66 def testNowMixed2(self): 67 """ Test counting is not possible when an exclsuive has the lock """ 68 lid = locks.MasterLock('dummy') 69 lac = locks.LockAccess(lid, 'counting') 70 lae = locks.LockAccess(lid, 'exclusive') 71 l = locks.BaseLock("name", maxCount=2) 72 self.failUnless(l.isAvailable(lae)) 73 l.claim("count-owner", lae) 74 self.failIf(l.isAvailable(lac)) 75 l.release("count-owner", lae) 76 self.failUnless(l.isAvailable(lae)) 77 78 def testLaterCounting(self): 79 lid = locks.MasterLock('dummy') 80 la = locks.LockAccess(lid, 'counting') 81 return self._testLater(la) 82 83 def testLaterExclusive(self): 84 lid = locks.MasterLock('dummy') 85 la = locks.LockAccess(lid, 'exclusive') 86 return self._testLater(la) 87 88 def _testLater(self, la): 89 lock = locks.BaseLock("name") 90 d = claimHarder(lock, "owner1", la) 91 d.addCallback(lambda lock: lock.release("owner1", la)) 92 return d 93 94 def testCompetitionCounting(self): 95 lid = locks.MasterLock('dummy') 96 la = locks.LockAccess(lid, 'counting') 97 return self._testCompetition(la) 98 99 def testCompetitionExclusive(self): 100 lid = locks.MasterLock('dummy') 101 la = locks.LockAccess(lid, 'exclusive') 102 return self._testCompetition(la) 103 104 def _testCompetition(self, la): 105 lock = locks.BaseLock("name") 106 d = claimHarder(lock, "owner1", la) 107 d.addCallback(self._claim1, la) 108 return d 109 def _claim1(self, lock, la): 57 110 # we should have claimed it by now 58 self.failIf(lock.isAvailable( ))111 self.failIf(lock.isAvailable(la)) 59 112 # now set up two competing owners. We don't know which will get the 60 113 # lock first. 61 d2 = claimHarder(lock, "owner2" )62 d2.addCallback(hold, "owner2", "now")63 d3 = claimHarder(lock, "owner3" )64 d3.addCallback(hold, "owner3", "soon")114 d2 = claimHarder(lock, "owner2", la) 115 d2.addCallback(hold, "owner2", la, "now") 116 d3 = claimHarder(lock, "owner3", la) 117 d3.addCallback(hold, "owner3", la, "soon") 65 118 dl = defer.DeferredList([d2,d3]) 66 dl.addCallback(self._cleanup, lock )119 dl.addCallback(self._cleanup, lock, la) 67 120 # and release the lock in a moment 68 reactor.callLater(0.1, lock.release, "owner1" )121 reactor.callLater(0.1, lock.release, "owner1", la) 69 122 return dl 70 123 71 def _cleanup(self, res, lock ):72 d = claimHarder(lock, "cleanup" )73 d.addCallback(lambda lock: lock.release("cleanup" ))124 def _cleanup(self, res, lock, la): 125 d = claimHarder(lock, "cleanup", la) 126 d.addCallback(lambda lock: lock.release("cleanup", la)) 74 127 return d 75 128 76 def testRandom(self): 129 def testRandomCounting(self): 130 lid = locks.MasterLock('dummy') 131 la = locks.LockAccess(lid, 'counting') 132 return self._testRandom(la) 133 134 def testRandomExclusive(self): 135 lid = locks.MasterLock('dummy') 136 la = locks.LockAccess(lid, 'exclusive') 137 return self._testRandom(la) 138 139 def _testRandom(self, la): 77 140 lock = locks.BaseLock("name") 78 141 dl = [] 79 142 for i in range(100): 80 143 owner = "owner%d" % i 81 144 mode = random.choice(["now", "very soon", "soon"]) 82 d = claimHarder(lock, owner )83 d.addCallback(hold, owner, mode)145 d = claimHarder(lock, owner, la) 146 d.addCallback(hold, owner, la, mode) 84 147 dl.append(d) 85 148 d = defer.DeferredList(dl) 86 d.addCallback(self._cleanup, lock )149 d.addCallback(self._cleanup, lock, la) 87 150 return d 88 151 89 152 class Multi(unittest.TestCase): 90 def testNow(self): 153 def testNowCounting(self): 154 lid = locks.MasterLock('dummy') 155 la = locks.LockAccess(lid, 'counting') 91 156 lock = locks.BaseLock("name", 2) 92 self.failUnless(lock.isAvailable( ))93 lock.claim("owner1" )94 self.failUnless(lock.isAvailable( ))95 lock.claim("owner2" )96 self.failIf(lock.isAvailable( ))97 lock.release("owner1" )98 self.failUnless(lock.isAvailable( ))99 lock.release("owner2" )100 self.failUnless(lock.isAvailable( ))157 self.failUnless(lock.isAvailable(la)) 158 lock.claim("owner1", la) 159 self.failUnless(lock.isAvailable(la)) 160 lock.claim("owner2", la) 161 self.failIf(lock.isAvailable(la)) 162 lock.release("owner1", la) 163 self.failUnless(lock.isAvailable(la)) 164 lock.release("owner2", la) 165 self.failUnless(lock.isAvailable(la)) 101 166 102 def testLater(self): 167 def testLaterCounting(self): 168 lid = locks.MasterLock('dummy') 169 la = locks.LockAccess(lid, 'counting') 103 170 lock = locks.BaseLock("name", 2) 104 lock.claim("owner1" )105 lock.claim("owner2" )106 d = claimHarder(lock, "owner3" )107 d.addCallback(lambda lock: lock.release("owner3" ))108 lock.release("owner2" )109 lock.release("owner1" )171 lock.claim("owner1", la) 172 lock.claim("owner2", la) 173 d = claimHarder(lock, "owner3", la) 174 d.addCallback(lambda lock: lock.release("owner3", la)) 175 lock.release("owner2", la) 176 lock.release("owner1", la) 110 177 return d 111 178 112 def _cleanup(self, res, lock, count ):179 def _cleanup(self, res, lock, count, la): 113 180 dl = [] 114 181 for i in range(count): 115 d = claimHarder(lock, "cleanup%d" % i )182 d = claimHarder(lock, "cleanup%d" % i, la) 116 183 dl.append(d) 117 184 d2 = defer.DeferredList(dl) 118 185 # once all locks are claimed, we know that any previous owners have 119 186 # been flushed out 120 187 def _release(res): 121 188 for i in range(count): 122 lock.release("cleanup%d" % i )189 lock.release("cleanup%d" % i, la) 123 190 d2.addCallback(_release) 124 191 return d2 125 192 126 def testRandom(self): 193 def testRandomCounting(self): 194 lid = locks.MasterLock('dummy') 195 la = locks.LockAccess(lid, 'counting') 127 196 COUNT = 5 128 197 lock = locks.BaseLock("name", COUNT) 129 198 dl = [] 130 199 for i in range(100): 131 200 owner = "owner%d" % i 132 201 mode = random.choice(["now", "very soon", "soon"]) 133 d = claimHarder(lock, owner )202 d = claimHarder(lock, owner, la) 134 203 def _check(lock): 135 204 self.failIf(len(lock.owners) > COUNT) 136 205 return lock 137 206 d.addCallback(_check) 138 d.addCallback(hold, owner, mode)207 d.addCallback(hold, owner, la, mode) 139 208 dl.append(d) 140 209 d = defer.DeferredList(dl) 141 d.addCallback(self._cleanup, lock, COUNT )210 d.addCallback(self._cleanup, lock, COUNT, la) 142 211 return d 143 212 144 213 class Dummy: -
buildbot-0.7.7/docs/buildbot.texinfo
old new 5423 5423 @cindex locks 5424 5424 @slindex buildbot.locks.MasterLock 5425 5425 @slindex buildbot.locks.SlaveLock 5426 @slindex buildbot.locks.LockAccess 5426 5427 5427 5428 For various reasons, you may want to prevent certain Steps (or perhaps 5428 5429 entire Builds) from running simultaneously. Limited CPU speed or … … 5442 5443 one-Build-at-a-time limit for each machine, but still allow as many 5443 5444 simultaneous builds as there are machines. 5444 5445 5446 When using a @code{Lock}, you can specify how you want to access it 5447 with a @code{LockAccess} object. You can have an exclusive lock (the 5448 @code{'exclusive'} mode) where the lock has exactly one owner, or 5449 share it with with others up to the limit (the @code{'counting'} 5450 mode). 5451 5445 5452 Each @code{Lock} is created with a unique name. Each lock gets a count 5446 of how many owners it may have : how many processes can claim it at the5453 of how many owners it may have in @code{'counting'} mode: how many processes can claim it at the 5447 5454 same time. This limit defaults to one, and is controllable through the 5448 5455 @code{maxCount} argument. On @code{SlaveLock}s you can set the owner 5449 5456 count on a per-slave basis by providing a dictionary (that maps from … … 5451 5458 argument. Any buildslaves that aren't mentioned in 5452 5459 @code{maxCountForSlave} get their owner count from @code{maxCount}. 5453 5460 5454 To use a lock, simply include it in the @code{locks=} argument of the5461 To use a lock, simply include it with the desired mode of access in the @code{locks=} argument of the 5455 5462 @code{BuildStep} object that should obtain the lock before it runs. 5456 This argument accepts a list of @code{Lock} objects: the Step will 5457 acquire all of them before it runs. 5463 This argument accepts a list of @code{LockAccess} objects: the Step will 5464 acquire all of them before it runs. (For backwards compatibility, the 5465 list also accepts @code{Lock} objects which are accessed in 5466 @code{'counting'} mode.) 5458 5467 5459 5468 To claim a lock for the whole Build, add a @code{'locks'} key to the 5460 builder specification dictionary with the same list of @code{Lock} 5461 objects. (This is the dictionary that has the @code{'name'}, 5469 builder specification dictionary with the same list of 5470 @code{LockAccess} objects (and for backwards compatibility, the list 5471 also accepts @code{Lock} objects which are accessed in 5472 @code{'counting'} mode). 5473 (The builder specification dictionary is the dictionary that has the @code{'name'}, 5462 5474 @code{'slavename'}, @code{'builddir'}, and @code{'factory'} keys). The 5463 5475 @code{Build} object also accepts a @code{locks=} argument, but unless 5464 5476 you are writing your own @code{BuildFactory} subclass then it will be … … 5492 5504 f = factory.BuildFactory() 5493 5505 f.addStep(source.SVN(svnurl="http://example.org/svn/Trunk")) 5494 5506 f.addStep(shell.ShellCommand(command="make all")) 5495 f.addStep(shell.ShellCommand(command="make test", locks=[db_lock])) 5507 f.addStep(shell.ShellCommand(command="make test", 5508 locks=[db_lock.access('exclusive')])) 5496 5509 b1 = @{'name': 'full1', 'slavename': 'bot-1', 'builddir': 'f1', 'factory': f@} 5497 5510 b2 = @{'name': 'full2', 'slavename': 'bot-2', 'builddir': 'f2', 'factory': f@} 5498 5511 b3 = @{'name': 'full3', 'slavename': 'bot-3', 'builddir': 'f3', 'factory': f@} 5499 5512 c['builders'] = [b1, b2, b3] 5500 5513 @end example 5501 5514 5515 Note that by default, a @code{Lock} uses a @code{maxCount=1} limit, so 5516 you can also use the @code{'counting'} access mode in this example. 5517 5502 5518 In the next example, we have one buildslave hosting three separate 5503 5519 Builders (each running tests against a different version of Python). 5504 5520 The machine which hosts this buildslave is not particularly fast, so … … 5523 5539 f23 = factory.Trial(source, trialpython=["python2.3"]) 5524 5540 f24 = factory.Trial(source, trialpython=["python2.4"]) 5525 5541 b1 = @{'name': 'p22', 'slavename': 'bot-1', 'builddir': 'p22', 5526 'factory': f22, 'locks': [slow_lock ] @}5542 'factory': f22, 'locks': [slow_lock.access('counting')] @} 5527 5543 b2 = @{'name': 'p23', 'slavename': 'bot-1', 'builddir': 'p23', 5528 'factory': f23, 'locks': [slow_lock ] @}5544 'factory': f23, 'locks': [slow_lock.access('counting')] @} 5529 5545 b3 = @{'name': 'p24', 'slavename': 'bot-1', 'builddir': 'p24', 5530 'factory': f24, 'locks': [slow_lock ] @}5546 'factory': f24, 'locks': [slow_lock.access('counting')] @} 5531 5547 c['builders'] = [b1, b2, b3] 5532 5548 @end example 5533 5549 5550 You can also mix access modes. In the next example, a limit of at most 5551 two builds is enforced. The cleanup process however needs exclusive 5552 access to prevent interference with the build processes. 5553 5554 @example 5555 from buildbot import locks 5556 from buildbot.steps import source, shell 5557 from buildbot.process import factory 5558 5559 build_lock = locks.MasterLock("builds", maxCount=2) 5560 fb = factory.BuildFactory() 5561 fb.addStep(source.SVN(svnurl="http://example.org/svn/Trunk")) 5562 fb.addStep(shell.ShellCommand(command=["make", "all"])) 5563 b1 = @{'name': 'b1', 'slavename': 'bot-1', 'builddir' : 'b1', 5564 'factory': fb, 'locks': [build_lock.access('counting')] @} 5565 b2 = @{'name': 'b2', 'slavename': 'bot-2', 'builddir' : 'b2', 5566 'factory': fb, 'locks': [build_lock.access('counting')] @} 5567 b3 = @{'name': 'b3', 'slavename': 'bot-3', 'builddir' : 'b3', 5568 'factory': fb, 'locks': [build_lock.access('counting')] @} 5569 fc = factory.BuildFactory() 5570 fc.addStep(shell.ShellCommand(command=["cleanup"])) 5571 cl = @{'name': 'clean', 'slavename': 'bot-1', 'builddir' :clean', 5572 'factory': fc, 'locks': [build_lock.access('exclusive')] @} 5573 c['builders'] = [b1, b2, b3, cl] 5574 @end example 5575 5534 5576 In the last example, we use two Locks at the same time. In this case, 5535 5577 we're concerned about both of the previous constraints, but we'll say 5536 5578 that only the tests are computationally intensive, and that they have … … 5551 5593 cpu_lock = locks.SlaveLock("cpu", maxCountForSlave=slavecounts) 5552 5594 f = factory.BuildFactory() 5553 5595 f.addStep(source.SVN(svnurl="http://example.org/svn/Trunk")) 5554 f.addStep(shell.ShellCommand(command="make all", locks=[cpu_lock])) 5555 f.addStep(shell.ShellCommand(command="make test", locks=[cpu_lock])) 5556 f.addStep(shell.ShellCommand(command="make db-test", locks=[db_lock, cpu_lock])) 5596 f.addStep(shell.ShellCommand(command="make all", 5597 locks=[cpu_lock.access('counting')])) 5598 f.addStep(shell.ShellCommand(command="make test", 5599 locks=[cpu_lock.access('counting')])) 5600 f.addStep(shell.ShellCommand(command="make db-test", 5601 locks=[db_lock.access('exclusive'), 5602 cpu_lock.access('counting')])) 5557 5603 5558 5604 b1 = @{'name': 'full1', 'slavename': 'bot-slow', 'builddir': 'full1', 5559 5605 'factory': f@}
![[Buildbot Logo]](/trac/chrome/site/header-text-transparent.png)