Rev 3332: * ``LockDir`` lock acquisition and release now trigger hooks allowing in http://people.ubuntu.com/~robertc/baz2.0/physicallock.hooks

Robert Collins robertc at robertcollins.net
Fri Apr 4 03:15:28 BST 2008


At http://people.ubuntu.com/~robertc/baz2.0/physicallock.hooks

------------------------------------------------------------
revno: 3332
revision-id: robertc at robertcollins.net-20080404021333-13wfvylrgmvcp3gz
parent: pqm at pqm.ubuntu.com-20080403080121-tbx3clqp8wqe776c
committer: Robert Collins <robertc at robertcollins.net>
branch nick: PhysicalLock.hooks
timestamp: Fri 2008-04-04 13:13:33 +1100
message:
   * ``LockDir`` lock acquisition and release now trigger hooks allowing
     introspection of locking activity. (Robert Collins)
modified:
  NEWS                           NEWS-20050323055033-4e00b5db738777ff
  bzrlib/help_topics/en/hooks.txt hooks.txt-20070830033044-xxu2rced13f72dka-1
  bzrlib/lock.py                 lock.py-20050527050856-ec090bb51bc03349
  bzrlib/lockdir.py              lockdir.py-20060220222025-98258adf27fbdda3
  bzrlib/tests/test_lockdir.py   test_lockdir.py-20060220222025-33d4221569a3d600
=== modified file 'NEWS'
--- a/NEWS	2008-04-03 08:01:21 +0000
+++ b/NEWS	2008-04-04 02:13:33 +0000
@@ -44,6 +44,9 @@
       by forcing it to recv 64k at a time when reading lines in HTTP headers,
       rather than just 1 byte at a time.  (Andrew Bennetts)
 
+    * ``LockDir`` lock acquisition and release now trigger hooks allowing
+      introspection of locking activity. (Robert Collins)
+
     * Log --short and --line are much faster when -r is not specified.
       (Aaron Bentley)
 

=== modified file 'bzrlib/help_topics/en/hooks.txt'
--- a/bzrlib/help_topics/en/hooks.txt	2007-12-17 02:00:45 +0000
+++ b/bzrlib/help_topics/en/hooks.txt	2008-04-04 02:13:33 +0000
@@ -1,5 +1,5 @@
-Hooks
-=====
+Branch Hooks
+============
 
 post_push
 ---------
@@ -85,6 +85,23 @@
 The hook signature is (branch, revision_history), and the branch will be
 write-locked.
 
+
+PhysicalLock hooks
+==================
+
+These are hooked into via bzrlib.lock.hooks.
+
+acquired
+--------
+
+Called with a LockResult object when a lock has been successfully acquired.
+
+released
+--------
+
+Called with a LockResult object when a lock has been successfully released.
+
+
 See also `Using hooks`_ in the User Guide.
 
 .. _Using hooks: ../user-guide/index.html#using-hooks

=== modified file 'bzrlib/lock.py'
--- a/bzrlib/lock.py	2007-10-03 08:06:44 +0000
+++ b/bzrlib/lock.py	2008-04-04 02:13:33 +0000
@@ -42,6 +42,40 @@
     osutils,
     trace,
     )
+from bzrlib.hooks import Hooks
+
+
+class PhysicalLockHooks(Hooks):
+    """hooks for physical lock activity."""
+
+    def __init__(self):
+        """Create the default hooks.
+
+        There are no default hooks present.
+        """
+        Hooks.__init__(self)
+        # Introduced in 1.4:
+        # invoked when a physical lock has been successfully acquired.
+        self['acquired'] = []
+        # Introduced in 1.5:
+        # invoked when a physical lock has been successfully released.
+        self['released'] = []
+
+
+# The hooks instance clients should register against. Do *NOT* import this
+# symbol, always reference it through lock.hooks.
+hooks = PhysicalLockHooks()
+
+
+class LockResult(object):
+
+    def __init__(self, lock, details=None):
+        """Create a lock result for lock with optional details about the lock."""
+        self.lock = lock
+        self.details = details
+
+    def __eq__(self, other):
+        return self.lock == other.lock and self.details == other.details
 
 
 class _OSLock(object):

=== modified file 'bzrlib/lockdir.py'
--- a/bzrlib/lockdir.py	2007-10-03 08:06:44 +0000
+++ b/bzrlib/lockdir.py	2008-04-04 02:13:33 +0000
@@ -110,6 +110,7 @@
 from bzrlib import (
     debug,
     errors,
+    lock,
     )
 import bzrlib.config
 from bzrlib.errors import (
@@ -298,6 +299,7 @@
             self._locked_via_token = False
             self._lock_held = False
         else:
+            old_nonce = self.nonce
             # rename before deleting, because we can't atomically remove the
             # whole tree
             start_time = time.time()
@@ -323,6 +325,9 @@
                 self.transport.delete_tree(tmpname)
             self._trace("... unlock succeeded after %dms",
                     (time.time() - start_time) * 1000)
+            result = lock.LockResult(self, old_nonce)
+            for hook in lock.hooks['released']:
+                hook(result)
 
     def break_lock(self):
         """Break a lock not held by this instance of LockDir.
@@ -455,7 +460,11 @@
         """
         if self._fake_read_lock:
             raise LockContention(self)
-        return self._attempt_lock()
+        result = self._attempt_lock()
+        hook_result = lock.LockResult(self, result)
+        for hook in lock.hooks['acquired']:
+            hook(hook_result)
+        return result
 
     def wait_lock(self, timeout=None, poll=None, max_attempts=None):
         """Wait a certain period for a lock.

=== modified file 'bzrlib/tests/test_lockdir.py'
--- a/bzrlib/tests/test_lockdir.py	2007-12-17 15:17:28 +0000
+++ b/bzrlib/tests/test_lockdir.py	2008-04-04 02:13:33 +0000
@@ -25,6 +25,7 @@
 from bzrlib import (
     config,
     errors,
+    lock,
     osutils,
     tests,
     transport,
@@ -645,3 +646,68 @@
         self.assertRaises(errors.LockContention, ld2.attempt_lock)
         # no kibble
         check_dir(['held'])
+
+    def record_hook(self, result):
+        self._calls.append(result)
+
+    def reset_hooks(self):
+        self._old_hooks = lock.hooks
+        self.addCleanup(self.restore_hooks)
+        lock.hooks = lock.PhysicalLockHooks()
+
+    def restore_hooks(self):
+        lock.hooks = self._old_hooks
+
+    def test_PhysicalLock_dot_acquired_success(self):
+        # the PhysicalLock.acquired hook fires when a lock is acquired.
+        self._calls = []
+        self.reset_hooks()
+        lock.hooks.install_hook('acquired', self.record_hook)
+        ld = self.get_lock()
+        ld.create()
+        self.assertEqual([], self._calls)
+        result = ld.attempt_lock()
+        self.assertEqual([lock.LockResult(ld, result)], self._calls)
+        ld.unlock()
+        self.assertEqual([lock.LockResult(ld, result)], self._calls)
+
+    def test_PhysicalLock_dot_acquired_fail(self):
+        # the PhysicalLock.acquired hook does not fire on failure.
+        self._calls = []
+        self.reset_hooks()
+        ld = self.get_lock()
+        ld.create()
+        ld2 = self.get_lock()
+        ld2.attempt_lock()
+        # install a lock hook now, when the disk lock is locked
+        lock.hooks.install_hook('acquired', self.record_hook)
+        self.assertRaises(errors.LockContention, ld.attempt_lock)
+        self.assertEqual([], self._calls)
+        ld2.unlock()
+        self.assertEqual([], self._calls)
+
+    def test_PhysicalLock_dot_released_success(self):
+        # the PhysicalLock.released hook fires when a lock is acquired.
+        self._calls = []
+        self.reset_hooks()
+        lock.hooks.install_hook('released', self.record_hook)
+        ld = self.get_lock()
+        ld.create()
+        self.assertEqual([], self._calls)
+        result = ld.attempt_lock()
+        self.assertEqual([], self._calls)
+        ld.unlock()
+        self.assertEqual([lock.LockResult(ld, result)], self._calls)
+
+    def test_PhysicalLock_dot_released_fail(self):
+        # the PhysicalLock.released hook does not fire on failure.
+        self._calls = []
+        self.reset_hooks()
+        ld = self.get_lock()
+        ld.create()
+        ld2 = self.get_lock()
+        ld.attempt_lock()
+        ld2.force_break(ld2.peek())
+        lock.hooks.install_hook('released', self.record_hook)
+        self.assertRaises(LockBroken, ld.unlock)
+        self.assertEqual([], self._calls)




More information about the bazaar-commits mailing list