Commit c6a622cf authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

locking: Allow checking if lock is owned in certain mode



With this patch the “LockSet” and “GanetiLockManager” classes have a new
function to check if a single or a group of locks (at a certain level)
have been acquired in a specific mode. This will be used for additional
assertions. Until now they could only check if a lock has been acquired,
but not in which mode. One use-case will be updating the node state in
various places, where the node lock must be acquired in exclusive mode.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent c1ef933c
......@@ -943,9 +943,45 @@ class LockSet:
return self.__lockdict
def is_owned(self):
"""Is the current thread a current level owner?"""
"""Is the current thread a current level owner?
@note: Use L{check_owned} to check if a specific lock is held
"""
return threading.currentThread() in self.__owners
def check_owned(self, names, shared=-1):
"""Check if locks are owned in a specific mode.
@type names: sequence or string
@param names: Lock names (or a single lock name)
@param shared: See L{SharedLock.is_owned}
@rtype: bool
@note: Use L{is_owned} to check if the current thread holds I{any} lock and
L{list_owned} to get the names of all owned locks
"""
if isinstance(names, basestring):
names = [names]
# Avoid check if no locks are owned anyway
if names and self.is_owned():
candidates = []
# Gather references to all locks (in case they're deleted in the meantime)
for lname in names:
try:
lock = self.__lockdict[lname]
except KeyError:
raise errors.LockError("Non-existing lock '%s' in set '%s' (it may"
" have been removed)" % (lname, self.name))
else:
candidates.append(lock)
return compat.all(lock.is_owned(shared=shared) for lock in candidates)
else:
return False
def _add_owned(self, name=None):
"""Note the current thread owns the given lock"""
if name is None:
......@@ -1512,6 +1548,14 @@ class GanetiLockManager:
"""
return self.__keyring[level].list_owned()
def check_owned(self, level, names, shared=-1):
"""Check if locks at a certain level are owned in a specific mode.
@see: L{LockSet.check_owned}
"""
return self.__keyring[level].check_owned(names, shared=shared)
def _upper_owned(self, level):
"""Check that we don't own any lock at a level greater than the given one.
......
......@@ -1035,18 +1035,50 @@ class TestLockSet(_ThreadedTestCase):
newls = locking.LockSet([], "TestLockSet.testResources")
self.assertEquals(newls._names(), set())
def testCheckOwnedUnknown(self):
self.assertFalse(self.ls.check_owned("certainly-not-owning-this-one"))
for shared in [-1, 0, 1, 6378, 24255]:
self.assertFalse(self.ls.check_owned("certainly-not-owning-this-one",
shared=shared))
def testCheckOwnedUnknownWhileHolding(self):
self.assertFalse(self.ls.check_owned([]))
self.ls.acquire("one", shared=1)
self.assertRaises(errors.LockError, self.ls.check_owned, "nonexist")
self.assertTrue(self.ls.check_owned("one", shared=1))
self.assertFalse(self.ls.check_owned("one", shared=0))
self.assertFalse(self.ls.check_owned(["one", "two"]))
self.assertRaises(errors.LockError, self.ls.check_owned,
["one", "nonexist"])
self.assertRaises(errors.LockError, self.ls.check_owned, "")
self.ls.release()
self.assertFalse(self.ls.check_owned([]))
self.assertFalse(self.ls.check_owned("one"))
def testAcquireRelease(self):
self.assertFalse(self.ls.check_owned(self.ls._names()))
self.assert_(self.ls.acquire('one'))
self.assertEquals(self.ls.list_owned(), set(['one']))
self.assertTrue(self.ls.check_owned("one"))
self.assertTrue(self.ls.check_owned("one", shared=0))
self.assertFalse(self.ls.check_owned("one", shared=1))
self.ls.release()
self.assertEquals(self.ls.list_owned(), set())
self.assertFalse(self.ls.check_owned(self.ls._names()))
self.assertEquals(self.ls.acquire(['one']), set(['one']))
self.assertEquals(self.ls.list_owned(), set(['one']))
self.ls.release()
self.assertEquals(self.ls.list_owned(), set())
self.ls.acquire(['one', 'two', 'three'])
self.assertEquals(self.ls.list_owned(), set(['one', 'two', 'three']))
self.assertTrue(self.ls.check_owned(self.ls._names()))
self.assertTrue(self.ls.check_owned(self.ls._names(), shared=0))
self.assertFalse(self.ls.check_owned(self.ls._names(), shared=1))
self.ls.release('one')
self.assertFalse(self.ls.check_owned(["one"]))
self.assertTrue(self.ls.check_owned(["two", "three"]))
self.assertTrue(self.ls.check_owned(["two", "three"], shared=0))
self.assertFalse(self.ls.check_owned(["two", "three"], shared=1))
self.assertEquals(self.ls.list_owned(), set(['two', 'three']))
self.ls.release(['three'])
self.assertEquals(self.ls.list_owned(), set(['two']))
......@@ -1056,6 +1088,8 @@ class TestLockSet(_ThreadedTestCase):
self.assertEquals(self.ls.list_owned(), set(['one', 'three']))
self.ls.release()
self.assertEquals(self.ls.list_owned(), set())
for name in self.ls._names():
self.assertFalse(self.ls.check_owned(name))
def testNoDoubleAcquire(self):
self.ls.acquire('one')
......@@ -1504,11 +1538,20 @@ class TestLockSet(_ThreadedTestCase):
self.assertFalse(compat.any(i.is_owned()
for i in self.ls._get_lockdict().values()))
self.assertFalse(self.ls.check_owned(self.ls._names()))
for name in self.ls._names():
self.assertFalse(self.ls.check_owned(name))
self.assertEquals(self.ls.acquire(None, shared=0),
set(["one", "two", "three"]))
self.assertRaises(AssertionError, self.ls.downgrade, "unknown lock")
self.assertTrue(self.ls.check_owned(self.ls._names(), shared=0))
for name in self.ls._names():
self.assertTrue(self.ls.check_owned(name))
self.assertTrue(self.ls.check_owned(name, shared=0))
self.assertFalse(self.ls.check_owned(name, shared=1))
self.assertTrue(self.ls._get_lock().is_owned(shared=0))
self.assertTrue(compat.all(i.is_owned(shared=0)
for i in self.ls._get_lockdict().values()))
......@@ -1520,6 +1563,12 @@ class TestLockSet(_ThreadedTestCase):
for name, lock in
self.ls._get_lockdict().items()))
self.assertFalse(self.ls.check_owned("one", shared=0))
self.assertTrue(self.ls.check_owned("one", shared=1))
self.assertTrue(self.ls.check_owned("two", shared=0))
self.assertTrue(self.ls.check_owned("three", shared=0))
# Downgrade second lock
self.assertTrue(self.ls.downgrade(names="two"))
self.assertTrue(self.ls._get_lock().is_owned(shared=0))
should_share = lambda name: [0, 1][int(name in ("one", "two"))]
......@@ -1527,6 +1576,12 @@ class TestLockSet(_ThreadedTestCase):
for name, lock in
self.ls._get_lockdict().items()))
self.assertFalse(self.ls.check_owned("one", shared=0))
self.assertTrue(self.ls.check_owned("one", shared=1))
self.assertFalse(self.ls.check_owned("two", shared=0))
self.assertTrue(self.ls.check_owned("two", shared=1))
self.assertTrue(self.ls.check_owned("three", shared=0))
# Downgrading the last exclusive lock to shared must downgrade the
# lockset-internal lock too
self.assertTrue(self.ls.downgrade(names="three"))
......@@ -1534,6 +1589,10 @@ class TestLockSet(_ThreadedTestCase):
self.assertTrue(compat.all(i.is_owned(shared=1)
for i in self.ls._get_lockdict().values()))
# Verify owned locks
for name in self.ls._names():
self.assertTrue(self.ls.check_owned(name, shared=1))
# Downgrading a shared lock must be a no-op
self.assertTrue(self.ls.downgrade(names=["one", "three"]))
self.assertTrue(self.ls._get_lock().is_owned(shared=1))
......@@ -1657,6 +1716,9 @@ class TestGanetiLockManager(_ThreadedTestCase):
self.GL.acquire(locking.LEVEL_INSTANCE, ['i1'])
self.GL.acquire(locking.LEVEL_NODEGROUP, ['g2'])
self.GL.acquire(locking.LEVEL_NODE, ['n1', 'n2'], shared=1)
self.assertTrue(self.GL.check_owned(locking.LEVEL_NODE, ["n1", "n2"],
shared=1))
self.assertFalse(self.GL.check_owned(locking.LEVEL_INSTANCE, ["i1", "i3"]))
self.GL.release(locking.LEVEL_NODE, ['n2'])
self.assertEquals(self.GL.list_owned(locking.LEVEL_NODE), set(['n1']))
self.assertEquals(self.GL.list_owned(locking.LEVEL_NODEGROUP), set(['g2']))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment