283
def test_34_lock_write_waits(self):
284
"""LockDir.lock_write() will wait for the lock."""
285
t = self.get_transport()
286
lf1 = LockDir(t, 'test_lock')
290
def wait_and_unlock():
293
unlocker = Thread(target=wait_and_unlock)
296
lf2 = LockDir(t, 'test_lock')
297
self.setup_log_reporter(lf2)
305
# There should be only 1 report, even though it should have to
307
lock_base = lf2.transport.abspath(lf2.path)
308
self.assertEqual(1, len(self._logged_reports))
309
self.assertEqual('%s %s\n'
311
'Will continue to try until %s\n',
312
self._logged_reports[0][0])
313
args = self._logged_reports[0][1]
314
self.assertEqual('Unable to obtain', args[0])
315
self.assertEqual('lock %s' % (lock_base,), args[1])
316
self.assertStartsWith(args[2], 'held by ')
317
self.assertStartsWith(args[3], 'locked ')
318
self.assertEndsWith(args[3], ' ago')
319
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
321
def test_35_wait_lock_changing(self):
322
"""LockDir.wait_lock() will report if the lock changes underneath.
324
This is the stages we want to happen:
326
0) Synchronization locks are created and locked.
327
1) Lock1 obtains the lockdir, and releases the 'check' lock.
328
2) Lock2 grabs the 'check' lock, and checks the lockdir.
329
It sees the lockdir is already acquired, reports the fact,
330
and unsets the 'checked' lock.
331
3) Thread1 blocks on acquiring the 'checked' lock, and then tells
332
Lock1 to release and acquire the lockdir. This resets the 'check'
334
4) Lock2 acquires the 'check' lock, and checks again. It notices
335
that the holder of the lock has changed, and so reports a new
337
5) Thread1 blocks on the 'checked' lock, this time, it completely
338
unlocks the lockdir, allowing Lock2 to acquire the lock.
341
wait_to_check_lock = Lock()
342
wait_until_checked_lock = Lock()
344
wait_to_check_lock.acquire()
345
wait_until_checked_lock.acquire()
346
note('locked check and checked locks')
348
class LockDir1(LockDir):
349
"""Use the synchronization points for the first lock."""
351
def attempt_lock(self):
352
# Once we have acquired the lock, it is okay for
353
# the other lock to check it
355
return super(LockDir1, self).attempt_lock()
357
note('lock1: releasing check lock')
358
wait_to_check_lock.release()
360
class LockDir2(LockDir):
361
"""Use the synchronization points for the second lock."""
363
def attempt_lock(self):
364
note('lock2: waiting for check lock')
365
wait_to_check_lock.acquire()
366
note('lock2: acquired check lock')
368
return super(LockDir2, self).attempt_lock()
370
note('lock2: releasing checked lock')
371
wait_until_checked_lock.release()
373
t = self.get_transport()
374
lf1 = LockDir1(t, 'test_lock')
377
lf2 = LockDir2(t, 'test_lock')
378
self.setup_log_reporter(lf2)
380
def wait_and_switch():
382
# Block until lock2 has had a chance to check
383
note('lock1: waiting 1 for checked lock')
384
wait_until_checked_lock.acquire()
385
note('lock1: acquired for checked lock')
386
note('lock1: released lockdir')
388
note('lock1: acquiring lockdir')
389
# Create a new nonce, so the lock looks different.
390
lf1.nonce = osutils.rand_chars(20)
392
note('lock1: acquired lockdir')
394
# Block until lock2 has peeked again
395
note('lock1: waiting 2 for checked lock')
396
wait_until_checked_lock.acquire()
397
note('lock1: acquired for checked lock')
398
# Now unlock, and let lock 2 grab the lock
400
wait_to_check_lock.release()
402
unlocker = Thread(target=wait_and_switch)
405
# Wait and play against the other thread
406
lf2.wait_lock(timeout=1.0, poll=0.01)
411
# There should be 2 reports, because the lock changed
412
lock_base = lf2.transport.abspath(lf2.path)
413
self.assertEqual(2, len(self._logged_reports))
415
self.assertEqual('%s %s\n'
417
'Will continue to try until %s\n',
418
self._logged_reports[0][0])
419
args = self._logged_reports[0][1]
420
self.assertEqual('Unable to obtain', args[0])
421
self.assertEqual('lock %s' % (lock_base,), args[1])
422
self.assertStartsWith(args[2], 'held by ')
423
self.assertStartsWith(args[3], 'locked ')
424
self.assertEndsWith(args[3], ' ago')
425
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
427
self.assertEqual('%s %s\n'
429
'Will continue to try until %s\n',
430
self._logged_reports[1][0])
431
args = self._logged_reports[1][1]
432
self.assertEqual('Lock owner changed for', args[0])
433
self.assertEqual('lock %s' % (lock_base,), args[1])
434
self.assertStartsWith(args[2], 'held by ')
435
self.assertStartsWith(args[3], 'locked ')
436
self.assertEndsWith(args[3], ' ago')
437
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
239
439
def test_40_confirm_easy(self):
240
440
"""Confirm a lock that's already held"""
241
441
t = self.get_transport()