diff options
author | Rick Harris <rick.harris@rackspace.com> | 2011-03-24 21:13:55 +0000 |
---|---|---|
committer | Rick Harris <rick.harris@rackspace.com> | 2011-03-24 21:13:55 +0000 |
commit | 4793991cdfa171839ddbc40e513b9625a1c89bbe (patch) | |
tree | 7d6a5e6293e06ee27fa6d0de302c5caf84be903d /nova/utils.py | |
parent | 07af0c9653863575600986158b89ff6afa48996e (diff) | |
parent | ab997441766f3d7454706ea9d630958287f53f01 (diff) | |
download | nova-4793991cdfa171839ddbc40e513b9625a1c89bbe.tar.gz nova-4793991cdfa171839ddbc40e513b9625a1c89bbe.tar.xz nova-4793991cdfa171839ddbc40e513b9625a1c89bbe.zip |
Merging trunk
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 d114cb14f..3d65a88ec 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 @@ -635,3 +703,12 @@ def subset_dict(dict_, keys): """Return a dict that only contains a subset of keys""" subset = partition_dict(dict_, keys)[0] return subset + + +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 |