summaryrefslogtreecommitdiffstats
path: root/nova/utils.py
diff options
context:
space:
mode:
authorVishvananda Ishaya <vishvananda@gmail.com>2012-03-16 13:25:05 -0700
committerVishvananda Ishaya <vishvananda@gmail.com>2012-03-16 13:56:17 -0700
commiteb42e7fcd7bb67ab951c9bc6c80a78cd23011458 (patch)
treee6813969a63010220c32880c6b645fd2f4438563 /nova/utils.py
parent1ecf2c5b77d21015bbb4cc9edf7abf96355bb8e3 (diff)
downloadnova-eb42e7fcd7bb67ab951c9bc6c80a78cd23011458.tar.gz
nova-eb42e7fcd7bb67ab951c9bc6c80a78cd23011458.tar.xz
nova-eb42e7fcd7bb67ab951c9bc6c80a78cd23011458.zip
Workaround issue with greenthreads and lockfiles
* Adds a GreenLockFile that always works in greenthreads * Adds test to verify that regular Lockfile is broken * Adds test to verify that GreenLockfile works * Adds note about limitation of external locks * Adds test showing limitation of nested locks * Fixes bug 956313 Change-Id: I11cd1206611aa4862dadd2fcc077c4c2e0f798f6
Diffstat (limited to 'nova/utils.py')
-rw-r--r--nova/utils.py44
1 files changed, 43 insertions, 1 deletions
diff --git a/nova/utils.py b/nova/utils.py
index e375f11e5..a23feea73 100644
--- a/nova/utils.py
+++ b/nova/utils.py
@@ -36,12 +36,14 @@ import socket
import struct
import sys
import tempfile
+import threading
import time
import types
import uuid
import warnings
from xml.sax import saxutils
+from eventlet import corolocal
from eventlet import event
from eventlet import greenthread
from eventlet import semaphore
@@ -838,6 +840,33 @@ else:
anyjson.force_implementation("nova.utils")
+class GreenLockFile(lockfile.FileLock):
+ """Implementation of lockfile that allows for a lock per greenthread.
+
+ Simply implements lockfile:LockBase init with an addiontall suffix
+ on the unique name of the greenthread identifier
+ """
+ def __init__(self, path, threaded=True):
+ self.path = path
+ self.lock_file = os.path.abspath(path) + ".lock"
+ self.hostname = socket.gethostname()
+ self.pid = os.getpid()
+ if threaded:
+ t = threading.current_thread()
+ # Thread objects in Python 2.4 and earlier do not have ident
+ # attrs. Worm around that.
+ ident = getattr(t, "ident", hash(t))
+ gident = corolocal.get_ident()
+ self.tname = "-%x-%x" % (ident & 0xffffffff, gident & 0xffffffff)
+ else:
+ self.tname = ""
+ dirname = os.path.dirname(self.lock_file)
+ self.unique_name = os.path.join(dirname,
+ "%s%s.%s" % (self.hostname,
+ self.tname,
+ self.pid))
+
+
_semaphores = {}
@@ -869,6 +898,19 @@ def synchronized(name, external=False):
a method decorated with @synchronized('mylock', external=True), only one
of them will execute at a time.
+ Important limitation: you can only have one external lock running per
+ thread at a time. For example the following will fail:
+
+ @utils.synchronized('testlock1', external=True)
+ def outer_lock():
+
+ @utils.synchronized('testlock2', external=True)
+ def inner_lock():
+ pass
+ inner_lock()
+
+ outer_lock()
+
"""
def wrap(f):
@@ -893,7 +935,7 @@ def synchronized(name, external=False):
{'lock': name, 'method': f.__name__})
lock_file_path = os.path.join(FLAGS.lock_path,
'nova-%s' % name)
- lock = lockfile.FileLock(lock_file_path)
+ lock = GreenLockFile(lock_file_path)
with lock:
LOG.debug(_('Got file lock "%(lock)s" for '
'method "%(method)s"...') %