diff options
author | Sandy Walsh <sandy.walsh@rackspace.com> | 2011-03-24 05:10:01 -0700 |
---|---|---|
committer | Sandy Walsh <sandy.walsh@rackspace.com> | 2011-03-24 05:10:01 -0700 |
commit | 1a7f2e4b817ec7d57d9ae396c34e6103ef972226 (patch) | |
tree | ba71a274608090c543e9ee59712e76ca31dbd602 /nova/utils.py | |
parent | f640d32bd8698fc2c30b2ca0454672d691f9b296 (diff) | |
parent | 2434138bbc73a8dbaee44c66cb7bed9f1fa40b2b (diff) | |
download | nova-1a7f2e4b817ec7d57d9ae396c34e6103ef972226.tar.gz nova-1a7f2e4b817ec7d57d9ae396c34e6103ef972226.tar.xz nova-1a7f2e4b817ec7d57d9ae396c34e6103ef972226.zip |
trunk merge
Diffstat (limited to 'nova/utils.py')
-rw-r--r-- | nova/utils.py | 91 |
1 files changed, 84 insertions, 7 deletions
diff --git a/nova/utils.py b/nova/utils.py index 499af2039..e4d8a70eb 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -41,6 +41,7 @@ from xml.sax import saxutils from eventlet import event from eventlet import greenthread +from eventlet import semaphore from eventlet.green import subprocess None from nova import exception @@ -334,6 +335,14 @@ def utcnow(): utcnow.override_time = None +def is_older_than(before, seconds): + """Return True if before is older than 'seconds'""" + if utcnow() - before > datetime.timedelta(seconds=seconds): + return True + else: + return False + + def utcnow_ts(): """Timestamp version of our utcnow function.""" return time.mktime(utcnow().timetuple()) @@ -531,17 +540,76 @@ def loads(s): return json.loads(s) -def synchronized(name): +_semaphores = {} + + +class _NoopContextManager(object): + def __enter__(self): + pass + + def __exit__(self, exc_type, exc_val, exc_tb): + pass + + +def synchronized(name, external=False): + """Synchronization decorator + + Decorating a method like so: + @synchronized('mylock') + def foo(self, *args): + ... + + ensures that only one thread will execute the bar method at a time. + + Different methods can share the same lock: + @synchronized('mylock') + def foo(self, *args): + ... + + @synchronized('mylock') + def bar(self, *args): + ... + + This way only one of either foo or bar can be executing at a time. + + The external keyword argument denotes whether this lock should work across + multiple processes. This means that if two different workers both run a + a method decorated with @synchronized('mylock', external=True), only one + of them will execute at a time. + """ + def wrap(f): @functools.wraps(f) def inner(*args, **kwargs): - LOG.debug(_("Attempting to grab %(lock)s for method " - "%(method)s..." % {"lock": name, + # NOTE(soren): If we ever go natively threaded, this will be racy. + # See http://stackoverflow.com/questions/5390569/dyn\ + # amically-allocating-and-destroying-mutexes + if name not in _semaphores: + _semaphores[name] = semaphore.Semaphore() + sem = _semaphores[name] + LOG.debug(_('Attempting to grab semaphore "%(lock)s" for method ' + '"%(method)s"...' % {"lock": name, "method": f.__name__})) - lock = lockfile.FileLock(os.path.join(FLAGS.lock_path, - 'nova-%s.lock' % name)) - with lock: - return f(*args, **kwargs) + with sem: + if external: + LOG.debug(_('Attempting to grab file lock "%(lock)s" for ' + 'method "%(method)s"...' % + {"lock": name, "method": f.__name__})) + lock_file_path = os.path.join(FLAGS.lock_path, + 'nova-%s.lock' % name) + lock = lockfile.FileLock(lock_file_path) + else: + lock = _NoopContextManager() + + with lock: + retval = f(*args, **kwargs) + + # If no-one else is waiting for it, delete it. + # See note about possible raciness above. + if not sem.balance < 1: + del _semaphores[name] + + return retval return inner return wrap @@ -593,3 +661,12 @@ def get_from_path(items, path): return results else: return get_from_path(results, remainder) + + +def check_isinstance(obj, cls): + """Checks that obj is of type cls, and lets PyLint infer types""" + if isinstance(obj, cls): + return obj + raise Exception(_("Expected object of type: %s") % (str(cls))) + # TODO(justinsb): Can we make this better?? + return cls() # Ugly PyLint hack |