Have the interprocess lock follow lock conventions

The python locking api specifies a acquire/release
function pair. It is nice to have the interprocess
lock api also follow said api so that its objects
can be used in areas where the standard threading
locks are used.

Change-Id: If393bec863ced8c9ef413b4baa3267d2a5b38416
This commit is contained in:
Joshua Harlow
2014-01-17 19:32:35 -08:00
parent ccc8ac9c05
commit fab74ff451
2 changed files with 54 additions and 3 deletions

View File

@@ -75,7 +75,7 @@ class _InterProcessLock(object):
self.lockfile = None self.lockfile = None
self.fname = name self.fname = name
def __enter__(self): def acquire(self):
basedir = os.path.dirname(self.fname) basedir = os.path.dirname(self.fname)
if not os.path.exists(basedir): if not os.path.exists(basedir):
@@ -92,7 +92,7 @@ class _InterProcessLock(object):
# to have a laughable 10 attempts "blocking" mechanism. # to have a laughable 10 attempts "blocking" mechanism.
self.trylock() self.trylock()
LOG.debug(_('Got file lock "%s"'), self.fname) LOG.debug(_('Got file lock "%s"'), self.fname)
return self return True
except IOError as e: except IOError as e:
if e.errno in (errno.EACCES, errno.EAGAIN): if e.errno in (errno.EACCES, errno.EAGAIN):
# external locks synchronise things like iptables # external locks synchronise things like iptables
@@ -101,7 +101,11 @@ class _InterProcessLock(object):
else: else:
raise raise
def __exit__(self, exc_type, exc_val, exc_tb): def __enter__(self):
self.acquire()
return self
def release(self):
try: try:
self.unlock() self.unlock()
self.lockfile.close() self.lockfile.close()
@@ -110,6 +114,9 @@ class _InterProcessLock(object):
self.fname) self.fname)
LOG.debug(_('Released file lock "%s"'), self.fname) LOG.debug(_('Released file lock "%s"'), self.fname)
def __exit__(self, exc_type, exc_val, exc_tb):
self.release()
def trylock(self): def trylock(self):
raise NotImplementedError() raise NotImplementedError()

View File

@@ -85,6 +85,50 @@ class LockTestCase(test.BaseTestCase):
self.assertEqual(foo.__name__, 'foo', "Wrapped function's name " self.assertEqual(foo.__name__, 'foo', "Wrapped function's name "
"got mangled") "got mangled")
def test_lock_acquire_release(self):
lock_dir = tempfile.mkdtemp()
lock_file = os.path.join(lock_dir, 'lock')
lock = lockutils.InterProcessLock(lock_file)
def try_lock():
lock.release() # child co-owns it before fork
try:
my_lock = lockutils.InterProcessLock(lock_file)
my_lock.lockfile = open(lock_file, 'w')
my_lock.trylock()
my_lock.unlock()
os._exit(1)
except IOError:
os._exit(0)
def attempt_acquire(count):
children = []
for i in range(count):
child = multiprocessing.Process(target=try_lock)
child.start()
children.append(child)
exit_codes = []
for child in children:
child.join()
exit_codes.append(child.exitcode)
return sum(exit_codes)
self.assertTrue(lock.acquire())
try:
acquired_children = attempt_acquire(10)
self.assertEqual(0, acquired_children)
finally:
lock.release()
try:
acquired_children = attempt_acquire(5)
self.assertNotEqual(0, acquired_children)
finally:
try:
shutil.rmtree(lock_dir)
except IOError:
pass
def test_lock_internally(self): def test_lock_internally(self):
"""We can lock across multiple green threads.""" """We can lock across multiple green threads."""
saved_sem_num = len(lockutils._semaphores) saved_sem_num = len(lockutils._semaphores)