summaryrefslogtreecommitdiff
path: root/oslo_concurrency/lockutils.py
diff options
context:
space:
mode:
Diffstat (limited to 'oslo_concurrency/lockutils.py')
-rw-r--r--oslo_concurrency/lockutils.py54
1 files changed, 44 insertions, 10 deletions
diff --git a/oslo_concurrency/lockutils.py b/oslo_concurrency/lockutils.py
index 67cf41e..1a9abe8 100644
--- a/oslo_concurrency/lockutils.py
+++ b/oslo_concurrency/lockutils.py
@@ -206,15 +206,35 @@ def remove_external_lock_file(name, lock_file_prefix=None, lock_path=None,
{'file': lock_file_path})
-def internal_lock(name, semaphores=None):
+class AcquireLockFailedException(Exception):
+ def __init__(self, lock_name):
+ self.message = "Failed to acquire the lock %s" % lock_name
+
+ def __str__(self):
+ return self.message
+
+
+def internal_lock(name, semaphores=None, blocking=True):
+ @contextlib.contextmanager
+ def nonblocking(lock):
+ """Try to acquire the internal lock without blocking."""
+ if not lock.acquire(blocking=False):
+ raise AcquireLockFailedException(name)
+ try:
+ yield lock
+ finally:
+ lock.release()
+
if semaphores is None:
semaphores = _semaphores
- return semaphores.get(name)
+ lock = semaphores.get(name)
+
+ return nonblocking(lock) if not blocking else lock
@contextlib.contextmanager
def lock(name, lock_file_prefix=None, external=False, lock_path=None,
- do_log=True, semaphores=None, delay=0.01, fair=False):
+ do_log=True, semaphores=None, delay=0.01, fair=False, blocking=True):
"""Context based lock
This function yields a `threading.Semaphore` instance (if we don't use
@@ -247,6 +267,10 @@ def lock(name, lock_file_prefix=None, external=False, lock_path=None,
:param fair: Whether or not we want a "fair" lock where contending lockers
will get the lock in the order in which they tried to acquire it.
+ :param blocking: Whether to wait forever to try to acquire the lock.
+ Incompatible with fair locks because those provided by the fasteners
+ module doesn't implements a non-blocking behavior.
+
.. versionchanged:: 0.2
Added *do_log* optional parameter.
@@ -257,17 +281,23 @@ def lock(name, lock_file_prefix=None, external=False, lock_path=None,
if semaphores is not None:
raise NotImplementedError(_('Specifying semaphores is not '
'supported when using fair locks.'))
- # The fastners module specifies that write_lock() provides fairness.
+ if blocking is not True:
+ raise NotImplementedError(_('Disabling blocking is not supported '
+ 'when using fair locks.'))
+ # The fasteners module specifies that write_lock() provides fairness.
int_lock = internal_fair_lock(name).write_lock()
else:
- int_lock = internal_lock(name, semaphores=semaphores)
+ int_lock = internal_lock(name, semaphores=semaphores,
+ blocking=blocking)
with int_lock:
if do_log:
LOG.debug('Acquired lock "%(lock)s"', {'lock': name})
try:
if external and not CONF.oslo_concurrency.disable_process_locking:
ext_lock = external_lock(name, lock_file_prefix, lock_path)
- ext_lock.acquire(delay=delay)
+ gotten = ext_lock.acquire(delay=delay, blocking=blocking)
+ if not gotten:
+ raise AcquireLockFailedException(name)
if do_log:
LOG.debug('Acquired external semaphore "%(lock)s"',
{'lock': name})
@@ -314,7 +344,7 @@ def lock_with_prefix(lock_file_prefix):
def synchronized(name, lock_file_prefix=None, external=False, lock_path=None,
- semaphores=None, delay=0.01, fair=False):
+ semaphores=None, delay=0.01, fair=False, blocking=True):
"""Synchronization decorator.
Decorating a method like so::
@@ -347,10 +377,11 @@ def synchronized(name, lock_file_prefix=None, external=False, lock_path=None,
def inner(*args, **kwargs):
t1 = timeutils.now()
t2 = None
+ gotten = True
try:
with lock(name, lock_file_prefix, external, lock_path,
do_log=False, semaphores=semaphores, delay=delay,
- fair=fair):
+ fair=fair, blocking=blocking):
t2 = timeutils.now()
LOG.debug('Lock "%(name)s" acquired by "%(function)s" :: '
'waited %(wait_secs)0.3fs',
@@ -358,15 +389,18 @@ def synchronized(name, lock_file_prefix=None, external=False, lock_path=None,
'function': reflection.get_callable_name(f),
'wait_secs': (t2 - t1)})
return f(*args, **kwargs)
+ except AcquireLockFailedException:
+ gotten = False
finally:
t3 = timeutils.now()
if t2 is None:
held_secs = "N/A"
else:
held_secs = "%0.3fs" % (t3 - t2)
- LOG.debug('Lock "%(name)s" released by "%(function)s" :: held '
- '%(held_secs)s',
+ LOG.debug('Lock "%(name)s" "%(gotten)s" by "%(function)s" ::'
+ ' held %(held_secs)s',
{'name': name,
+ 'gotten': 'released' if gotten else 'unacquired',
'function': reflection.get_callable_name(f),
'held_secs': held_secs})
return inner