582
class _InterProcessLock(object):
583
"""Lock implementation which allows multiple locks, working around
584
issues like bugs.debian.org/cgi-bin/bugreport.cgi?bug=632857 and does
585
not require any cleanup. Since the lock is always held on a file
586
descriptor rather than outside of the process, the lock gets dropped
587
automatically if the process crashes, even if __exit__ is not executed.
589
There are no guarantees regarding usage by multiple green threads in a
590
single process here. This lock works only between processes. Exclusive
591
access between local threads should be achieved using the semaphores
592
in the @synchronized decorator.
594
Note these locks are released when the descriptor is closed, so it's not
595
safe to close the file descriptor while another green thread holds the
596
lock. Just opening and closing the lock file can break synchronisation,
597
so lock files must be accessed only using this abstraction.
600
def __init__(self, name):
605
self.lockfile = open(self.fname, 'w')
609
# Using non-blocking locks since green threads are not
610
# patched to deal with blocking locking calls.
611
# Also upon reading the MSDN docs for locking(), it seems
612
# to have a laughable 10 attempts "blocking" mechanism.
616
if e.errno in (errno.EACCES, errno.EAGAIN):
617
# external locks synchronise things like iptables
618
# updates - give it some time to prevent busy spinning
623
def __exit__(self, exc_type, exc_val, exc_tb):
626
self.lockfile.close()
628
LOG.exception(_("Could not release the acquired lock `%s`")
632
raise NotImplementedError()
635
raise NotImplementedError()
638
class _WindowsLock(_InterProcessLock):
640
msvcrt.locking(self.lockfile, msvcrt.LK_NBLCK, 1)
643
msvcrt.locking(self.lockfile, msvcrt.LK_UNLCK, 1)
646
class _PosixLock(_InterProcessLock):
648
fcntl.lockf(self.lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
651
fcntl.lockf(self.lockfile, fcntl.LOCK_UN)
656
InterProcessLock = _WindowsLock
659
InterProcessLock = _PosixLock
661
_semaphores = weakref.WeakValueDictionary()
664
def synchronized(name, external=False, lock_path=None):
665
"""Synchronization decorator.
667
Decorating a method like so::
669
@synchronized('mylock')
670
def foo(self, *args):
673
ensures that only one thread will execute the bar method at a time.
675
Different methods can share the same lock::
677
@synchronized('mylock')
678
def foo(self, *args):
681
@synchronized('mylock')
682
def bar(self, *args):
685
This way only one of either foo or bar can be executing at a time.
687
The external keyword argument denotes whether this lock should work across
688
multiple processes. This means that if two different workers both run a
689
a method decorated with @synchronized('mylock', external=True), only one
690
of them will execute at a time.
692
The lock_path keyword argument is used to specify a special location for
693
external lock files to live. If nothing is set, then FLAGS.lock_path is
699
def inner(*args, **kwargs):
700
# NOTE(soren): If we ever go natively threaded, this will be racy.
701
# See http://stackoverflow.com/questions/5390569/dyn
702
# amically-allocating-and-destroying-mutexes
703
sem = _semaphores.get(name, semaphore.Semaphore())
704
if name not in _semaphores:
705
# this check is not racy - we're already holding ref locally
706
# so GC won't remove the item and there was no IO switch
707
# (only valid in greenthreads)
708
_semaphores[name] = sem
711
LOG.debug(_('Got semaphore "%(lock)s" for method '
712
'"%(method)s"...'), {'lock': name,
713
'method': f.__name__})
714
if external and not FLAGS.disable_process_locking:
715
LOG.debug(_('Attempting to grab file lock "%(lock)s" for '
716
'method "%(method)s"...'),
717
{'lock': name, 'method': f.__name__})
720
# We need a copy of lock_path because it is non-local
721
local_lock_path = lock_path
722
if not local_lock_path:
723
local_lock_path = FLAGS.lock_path
725
if not local_lock_path:
727
local_lock_path = tempfile.mkdtemp()
729
if not os.path.exists(local_lock_path):
731
ensure_tree(local_lock_path)
733
# NOTE(mikal): the lock name cannot contain directory
735
safe_name = name.replace(os.sep, '_')
736
lock_file_path = os.path.join(local_lock_path,
737
'nova-%s' % safe_name)
739
lock = InterProcessLock(lock_file_path)
741
LOG.debug(_('Got file lock "%(lock)s" for '
742
'method "%(method)s"...'),
743
{'lock': name, 'method': f.__name__})
744
retval = f(*args, **kwargs)
746
# NOTE(vish): This removes the tempdir if we needed
747
# to create one. This is used to cleanup
748
# the locks left behind by unit tests.
750
shutil.rmtree(local_lock_path)
752
retval = f(*args, **kwargs)
597
def to_bytes(text, default=0):
598
"""Try to turn a string into a number of bytes. Looks at the last
599
characters of the text to determine what conversion is needed to
600
turn the input text into a byte number.
602
Supports: B/b, K/k, M/m, G/g, T/t (or the same with b/B on the end)
605
# Take off everything not number 'like' (which should leave
606
# only the byte 'identifier' left)
607
mult_key_org = text.lstrip('-1234567890')
608
mult_key = mult_key_org.lower()
609
mult_key_len = len(mult_key)
610
if mult_key.endswith("b"):
611
mult_key = mult_key[0:-1]
613
multiplier = BYTE_MULTIPLIERS[mult_key]
615
# Empty cases shouldn't cause text[0:-0]
616
text = text[0:-mult_key_len]
617
return int(text) * multiplier
619
msg = _('Unknown byte multiplier: %s') % mult_key_org
759
625
def delete_if_exists(pathname):