Commit 42a999d1 authored by Guido Trotter's avatar Guido Trotter
Browse files

Locking: add ssynchronized decorator

This patch creates a new decorator function ssynchronized in the locking
library, which takes as input a SharedLock, and synchronizes access to
the decorated functions using it. The usual SharedLock semantics apply,
so it's possible to call more than one synchronized function at the same
time, when the lock is acquired in shared mode, and still protect
against exclusive access.

The patch also adds a few unit test to check the basic decorator's
functionality, and to provide an example on how to use it.

Reviewed-by: iustinp
parent 5b4cd1b0
......@@ -29,6 +29,25 @@ from ganeti import errors
from ganeti import utils
def ssynchronized(lock, shared=0):
"""Shared Synchronization decorator.
Calls the function holding the given lock, either in exclusive or shared
mode. It requires the passed lock to be a SharedLock (or support its
semantics).
"""
def wrap(fn):
def sync_function(*args, **kwargs):
lock.acquire(shared=shared)
try:
return fn(*args, **kwargs)
finally:
lock.release()
return sync_function
return wrap
class SharedLock:
"""Implements a shared lock.
......
......@@ -32,6 +32,11 @@ from ganeti import errors
from threading import Thread
# This is used to test the ssynchronize decorator.
# Since it's passed as input to a decorator it must be declared as a global.
_decoratorlock = locking.SharedLock()
class TestSharedLock(unittest.TestCase):
"""SharedLock tests"""
......@@ -230,6 +235,60 @@ class TestSharedLock(unittest.TestCase):
self.assertEqual(self.done.get(True, 1), 'ERR')
class TestSSynchronizedDecorator(unittest.TestCase):
"""Shared Lock Synchronized decorator test"""
def setUp(self):
# helper threads use the 'done' queue to tell the master they finished.
self.done = Queue.Queue(0)
@locking.ssynchronized(_decoratorlock)
def _doItExclusive(self):
self.assert_(_decoratorlock._is_owned())
self.done.put('EXC')
@locking.ssynchronized(_decoratorlock, shared=1)
def _doItSharer(self):
self.assert_(_decoratorlock._is_owned(shared=1))
self.done.put('SHR')
def testDecoratedFunctions(self):
self._doItExclusive()
self.assert_(not _decoratorlock._is_owned())
self._doItSharer()
self.assert_(not _decoratorlock._is_owned())
def testSharersCanCoexist(self):
_decoratorlock.acquire(shared=1)
Thread(target=self._doItSharer).start()
self.assert_(self.done.get(True, 1))
_decoratorlock.release()
def testExclusiveBlocksExclusive(self):
_decoratorlock.acquire()
Thread(target=self._doItExclusive).start()
# give it a bit of time to check that it's not actually doing anything
self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
_decoratorlock.release()
self.assert_(self.done.get(True, 1))
def testExclusiveBlocksSharer(self):
_decoratorlock.acquire()
Thread(target=self._doItSharer).start()
time.sleep(0.05)
self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
_decoratorlock.release()
self.assert_(self.done.get(True, 1))
def testSharerBlocksExclusive(self):
_decoratorlock.acquire(shared=1)
Thread(target=self._doItExclusive).start()
time.sleep(0.05)
self.assertRaises(Queue.Empty, self.done.get, True, 0.2)
_decoratorlock.release()
self.assert_(self.done.get(True, 1))
class TestLockSet(unittest.TestCase):
"""LockSet tests"""
......
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