2014-03-14 17:15:02 +00:00
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
|
|
import time
|
|
|
|
import os
|
2015-02-12 23:41:13 +00:00
|
|
|
import errno
|
2014-03-14 17:15:02 +00:00
|
|
|
|
|
|
|
from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
|
|
|
|
AlreadyLocked)
|
|
|
|
|
2015-02-12 23:41:13 +00:00
|
|
|
|
2014-03-14 17:15:02 +00:00
|
|
|
class LinkLockFile(LockBase):
|
2015-02-12 23:41:13 +00:00
|
|
|
"""
|
|
|
|
Lock access to a file using atomic property of link(2).
|
2014-03-14 17:15:02 +00:00
|
|
|
|
2015-02-12 23:41:13 +00:00
|
|
|
lock = LinkLockFile('somefile'[, threaded=False[, timeout=None]])
|
2014-03-14 17:15:02 +00:00
|
|
|
"""
|
|
|
|
|
2015-02-12 23:41:13 +00:00
|
|
|
# noinspection PyTypeChecker
|
2014-03-14 17:15:02 +00:00
|
|
|
def acquire(self, timeout=None):
|
|
|
|
try:
|
2015-02-12 23:41:13 +00:00
|
|
|
open(self.unique_name, 'wb').close()
|
2014-03-14 17:15:02 +00:00
|
|
|
except IOError:
|
2015-02-12 23:41:13 +00:00
|
|
|
raise LockFailed('failed to create %s' % self.unique_name)
|
2014-03-14 17:15:02 +00:00
|
|
|
|
|
|
|
timeout = timeout is not None and timeout or self.timeout
|
|
|
|
end_time = time.time()
|
|
|
|
if timeout is not None and timeout > 0:
|
|
|
|
end_time += timeout
|
|
|
|
|
|
|
|
while True:
|
|
|
|
# Try and create a hard link to it.
|
|
|
|
try:
|
|
|
|
os.link(self.unique_name, self.lock_file)
|
2015-02-12 23:41:13 +00:00
|
|
|
except OSError as e:
|
|
|
|
if errno.ENOSYS == e.errno:
|
|
|
|
raise LockFailed('%s' % e.strerror)
|
|
|
|
|
2014-03-14 17:15:02 +00:00
|
|
|
# Link creation failed. Maybe we've double-locked?
|
|
|
|
nlinks = os.stat(self.unique_name).st_nlink
|
|
|
|
if nlinks == 2:
|
|
|
|
# The original link plus the one I created == 2. We're
|
|
|
|
# good to go.
|
|
|
|
return
|
|
|
|
else:
|
|
|
|
# Otherwise the lock creation failed.
|
|
|
|
if timeout is not None and time.time() > end_time:
|
|
|
|
os.unlink(self.unique_name)
|
|
|
|
if timeout > 0:
|
2015-02-12 23:41:13 +00:00
|
|
|
raise LockTimeout('Timeout waiting to acquire lock for %s' % self.path)
|
2014-03-14 17:15:02 +00:00
|
|
|
else:
|
2015-02-12 23:41:13 +00:00
|
|
|
raise AlreadyLocked('%s is already locked' % self.path)
|
|
|
|
|
|
|
|
time.sleep(timeout is not None and (timeout / 10) or 0.1)
|
2014-03-14 17:15:02 +00:00
|
|
|
else:
|
|
|
|
# Link creation succeeded. We're good to go.
|
|
|
|
return
|
|
|
|
|
|
|
|
def release(self):
|
|
|
|
if not self.is_locked():
|
2015-02-12 23:41:13 +00:00
|
|
|
raise NotLocked('%s is not locked' % self.path)
|
2014-03-14 17:15:02 +00:00
|
|
|
elif not os.path.exists(self.unique_name):
|
2015-02-12 23:41:13 +00:00
|
|
|
raise NotMyLock('%s is locked, but not by me' % self.path)
|
2014-03-14 17:15:02 +00:00
|
|
|
os.unlink(self.unique_name)
|
|
|
|
os.unlink(self.lock_file)
|
|
|
|
|
|
|
|
def is_locked(self):
|
|
|
|
return os.path.exists(self.lock_file)
|
|
|
|
|
|
|
|
def i_am_locking(self):
|
|
|
|
return (self.is_locked() and
|
|
|
|
os.path.exists(self.unique_name) and
|
|
|
|
os.stat(self.unique_name).st_nlink == 2)
|
|
|
|
|
|
|
|
def break_lock(self):
|
|
|
|
if os.path.exists(self.lock_file):
|
|
|
|
os.unlink(self.lock_file)
|