"""lock module"""
from time import time, sleep
import threading
import fcntl
from abc import ABCMeta
def wait(delay):
delay = min(delay*2, .05)
sleep(delay)
return delay
class Lock(object):
"""A generic lock object, based on threading.Lock()."""
__metaclass__ = ABCMeta
def __init__(self):
self._locked = False
def acquire(self, blocking=True, timeout=None):
if blocking:
delay = 0.0005
if timeout is None:
while self._locked:
delay = wait(delay)
else:
end = time() + timeout
while time() < end:
if not self._locked:
break
delay = wait(delay)
else:
return False
self._locked = True
return True
elif timeout is not None:
raise ValueError("can't specify a timeout "
"for a non-blocking call")
if self._locked:
return False
self._locked = True
return True
def release(self):
if not self._locked:
raise RuntimeError("release unlocked lock")
self._locked = False
def locked(self):
return self._locked
# for the "with" statement
def __enter__(self): self.acquire()
def __exit__(self, *args): self.release()
Lock.register(type(threading.Lock()))
class ThreadingLock(Lock):
"""A subclass of Lock that wraps a threading lock."""
def __init__(self):
self._lock = threading.Lock()
def acquire(self, blocking=True, timeout=None):
if not blocking and timeout is not None:
raise ValueError("can't specify a timeout "
"for a non-blocking call")
if timeout is None:
return self._lock.acquire(blocking)
else:
#return self._lock.acquire(blocking, timeout)
raise NotImplementedError
def release(self):
self._lock.release()
def locked(self):
return self._lock.locked()
class LockableFile(object):
"""A POSIX file with filesystem locking."""
def __init__(self, file, mode="r"):
self._own = False
if isinstance(file, str):
file = open(file, mode)
self._own = True
self.file = file
self._locked = False
def __getattr__(self, name):
return getattr(self.file, name)
def lock(self, timeout=3):
if self._locked:
return
# couldn't get lock
error = None
end = time() + timeout
while time() < end:
try:
fcntl.lockf(self.file, fcntl.LOCK_EX | fcntl.LOCK_NB)
except IOError, e:
error = e
delay = wait(delay)
else:
break
else:
if error: raise error
self._locked = True
def unlock(self):
fcntl.lockf(self.file, fcntl.LOCK_UN)
self._locked = False
def __enter__(self):
self.lock()
def __exit__(self, *args):
self.unlock()
if self._own:
self.file.close()
class LockFile(Lock):
"""A Lock subclass implemented on top of a lock file."""
DEFAULT = "locked"
def __init__(self, path, value=DEFAULT):
self.path = path
self.default = value
def acquire(self, value=None, blocking=True, timeout=None):
if value is None:
value = self.default
if value is None:
raise TypeError("missing value")
try:
lockfile = LockableFile(self.path, "r+")
except IOError:
lockfile = LockableFile(self.path, "a+")
lockfile.seek(0)
with lockfile:
content = lockfile.read()
#if value is not self.DEFAULT and content == value:
# return True
if not content:
lockfile.seek(0)
lockfile.truncate()
lockfile.write(value)
return True
if blocking:
raise NotImplementedError
elif timeout is not None:
raise ValueError("can't specify a timeout "
"for a non-blocking call")
return False
def release(self, value=None):
if value is None:
value = self.default
value = str(value)
try:
lockfile = LockableFile(self.path, "r+")
except IOError:
return
with lockfile:
content = lockfile.read()
if content != value:
raise RuntimeError("got '%s', expected '%s'" %
(value, content))
lockfile.seek(0)
lockfile.truncate()
def value(self):
return open(self.path).read()
locked = value
def clear(self):
try:
lockfile = LockableFile(self.path, "r+")
except IOError:
return
lockfile.truncate()