Remove need to touch lock files before using.
- Locks will now create enclosing directories and touch the lock file automatically.
This commit is contained in:
parent
907fe912ef
commit
ea10e3bab0
6 changed files with 68 additions and 38 deletions
|
@ -52,12 +52,16 @@ class Lock(object):
|
|||
|
||||
"""
|
||||
|
||||
def __init__(self, file_path):
|
||||
self._file_path = file_path
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self._file = None
|
||||
self._reads = 0
|
||||
self._writes = 0
|
||||
|
||||
# PID and host of lock holder
|
||||
self.pid = self.old_pid = None
|
||||
self.host = self.old_host = None
|
||||
|
||||
def _lock(self, op, timeout):
|
||||
"""This takes a lock using POSIX locks (``fnctl.lockf``).
|
||||
|
||||
|
@ -83,15 +87,25 @@ def _lock(self, op, timeout):
|
|||
# Open reader locks read-only if possible.
|
||||
# lock doesn't exist, open RW + create if it doesn't exist.
|
||||
if self._file is None:
|
||||
mode = 'r+' if op == fcntl.LOCK_EX else 'r'
|
||||
self._file = open(self._file_path, mode)
|
||||
self._ensure_parent_directory()
|
||||
|
||||
os_mode, fd_mode = os.O_RDONLY, 'r'
|
||||
if op == fcntl.LOCK_EX or not os.path.exists(self.path):
|
||||
os_mode, fd_mode = (os.O_RDWR | os.O_CREAT), 'r+'
|
||||
|
||||
fd = os.open(self.path, os_mode)
|
||||
self._file = os.fdopen(fd, fd_mode)
|
||||
|
||||
# Try to get the lock (will raise if not available.)
|
||||
fcntl.lockf(self._file, op | fcntl.LOCK_NB)
|
||||
|
||||
# All locks read the owner PID and host
|
||||
self._read_lock_data()
|
||||
|
||||
# Exclusive locks write their PID/host
|
||||
if op == fcntl.LOCK_EX:
|
||||
self._file.write(
|
||||
"pid=%s,host=%s" % (os.getpid(), socket.getfqdn()))
|
||||
self._file.truncate()
|
||||
self._file.flush()
|
||||
self._write_lock_data()
|
||||
|
||||
return
|
||||
|
||||
except IOError as error:
|
||||
|
@ -103,6 +117,40 @@ def _lock(self, op, timeout):
|
|||
|
||||
raise LockError("Timed out waiting for lock.")
|
||||
|
||||
def _ensure_parent_directory(self):
|
||||
parent = os.path.dirname(self.path)
|
||||
try:
|
||||
os.makedirs(parent)
|
||||
return True
|
||||
except OSError as e:
|
||||
# makedirs can fail when diretory already exists.
|
||||
if not (e.errno == errno.EEXIST and os.path.isdir(parent) or
|
||||
e.errno == errno.EISDIR):
|
||||
raise
|
||||
|
||||
def _read_lock_data(self):
|
||||
"""Read PID and host data out of the file if it is there."""
|
||||
line = self._file.read()
|
||||
if line:
|
||||
pid, host = line.strip().split(',')
|
||||
_, _, self.pid = pid.rpartition('=')
|
||||
_, _, self.host = host.rpartition('=')
|
||||
|
||||
def _write_lock_data(self):
|
||||
"""Write PID and host data to the file, recording old values."""
|
||||
self.old_pid = self.pid
|
||||
self.old_host = self.host
|
||||
|
||||
self.pid = os.getpid()
|
||||
self.host = socket.getfqdn()
|
||||
|
||||
# write pid, host to disk to sync over FS
|
||||
self._file.seek(0)
|
||||
self._file.write("pid=%s,host=%s" % (self.pid, self.host))
|
||||
self._file.truncate()
|
||||
self._file.flush()
|
||||
os.fsync(self._file.fileno())
|
||||
|
||||
def _unlock(self):
|
||||
"""Releases a lock using POSIX locks (``fcntl.lockf``)
|
||||
|
||||
|
@ -126,7 +174,7 @@ def acquire_read(self, timeout=_default_timeout):
|
|||
|
||||
"""
|
||||
if self._reads == 0 and self._writes == 0:
|
||||
tty.debug('READ LOCK : {0._file_path} [Acquiring]'.format(self))
|
||||
tty.debug('READ LOCK : {0.path} [Acquiring]'.format(self))
|
||||
self._lock(fcntl.LOCK_SH, timeout) # can raise LockError.
|
||||
self._reads += 1
|
||||
return True
|
||||
|
@ -146,7 +194,7 @@ def acquire_write(self, timeout=_default_timeout):
|
|||
|
||||
"""
|
||||
if self._writes == 0:
|
||||
tty.debug('WRITE LOCK : {0._file_path} [Acquiring]'.format(self))
|
||||
tty.debug('WRITE LOCK : {0.path} [Acquiring]'.format(self))
|
||||
self._lock(fcntl.LOCK_EX, timeout) # can raise LockError.
|
||||
self._writes += 1
|
||||
return True
|
||||
|
@ -167,7 +215,7 @@ def release_read(self):
|
|||
assert self._reads > 0
|
||||
|
||||
if self._reads == 1 and self._writes == 0:
|
||||
tty.debug('READ LOCK : {0._file_path} [Released]'.format(self))
|
||||
tty.debug('READ LOCK : {0.path} [Released]'.format(self))
|
||||
self._unlock() # can raise LockError.
|
||||
self._reads -= 1
|
||||
return True
|
||||
|
@ -188,7 +236,7 @@ def release_write(self):
|
|||
assert self._writes > 0
|
||||
|
||||
if self._writes == 1 and self._reads == 0:
|
||||
tty.debug('WRITE LOCK : {0._file_path} [Released]'.format(self))
|
||||
tty.debug('WRITE LOCK : {0.path} [Released]'.format(self))
|
||||
self._unlock() # can raise LockError.
|
||||
self._writes -= 1
|
||||
return True
|
||||
|
|
|
@ -160,9 +160,6 @@ def __init__(self, root, db_dir=None):
|
|||
if not os.path.exists(self._db_dir):
|
||||
mkdirp(self._db_dir)
|
||||
|
||||
if not os.path.exists(self._lock_path):
|
||||
touch(self._lock_path)
|
||||
|
||||
# initialize rest of state.
|
||||
self.lock = Lock(self._lock_path)
|
||||
self._data = {}
|
||||
|
|
|
@ -77,10 +77,7 @@ def _lock_path(self, key):
|
|||
def _get_lock(self, key):
|
||||
"""Create a lock for a key, if necessary, and return a lock object."""
|
||||
if key not in self._locks:
|
||||
lock_file = self._lock_path(key)
|
||||
if not os.path.exists(lock_file):
|
||||
touch(lock_file)
|
||||
self._locks[key] = Lock(lock_file)
|
||||
self._locks[key] = Lock(self._lock_path(key))
|
||||
return self._locks[key]
|
||||
|
||||
def init_entry(self, key):
|
||||
|
|
|
@ -703,17 +703,8 @@ def prefix_lock(self):
|
|||
if self._prefix_lock is None:
|
||||
dirname = join_path(os.path.dirname(self.spec.prefix), '.locks')
|
||||
basename = os.path.basename(self.spec.prefix)
|
||||
lock_file = join_path(dirname, basename)
|
||||
|
||||
if not os.path.exists(lock_file):
|
||||
tty.debug('TOUCH FILE : {0}'.format(lock_file))
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except OSError:
|
||||
pass
|
||||
touch(lock_file)
|
||||
|
||||
self._prefix_lock = llnl.util.lock.Lock(lock_file)
|
||||
self._prefix_lock = llnl.util.lock.Lock(
|
||||
join_path(dirname, basename))
|
||||
|
||||
return self._prefix_lock
|
||||
|
||||
|
|
|
@ -150,15 +150,10 @@ def __init__(
|
|||
self.keep = keep
|
||||
|
||||
# File lock for the stage directory
|
||||
self._lock_file = None
|
||||
self._lock = None
|
||||
if lock:
|
||||
self._lock_file = join_path(spack.stage_path, self.name + '.lock')
|
||||
if not os.path.exists(self._lock_file):
|
||||
directory, _ = os.path.split(self._lock_file)
|
||||
mkdirp(directory)
|
||||
touch(self._lock_file)
|
||||
self._lock = llnl.util.lock.Lock(self._lock_file)
|
||||
self._lock = llnl.util.lock.Lock(
|
||||
join_path(spack.stage_path, self.name + '.lock'))
|
||||
|
||||
def __enter__(self):
|
||||
"""
|
||||
|
|
|
@ -44,7 +44,6 @@ class LockTest(unittest.TestCase):
|
|||
def setUp(self):
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.lock_path = join_path(self.tempdir, 'lockfile')
|
||||
touch(self.lock_path)
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.tempdir, ignore_errors=True)
|
||||
|
@ -172,14 +171,17 @@ def test_upgrade_read_to_write(self):
|
|||
lock.acquire_read()
|
||||
self.assertTrue(lock._reads == 1)
|
||||
self.assertTrue(lock._writes == 0)
|
||||
self.assertTrue(lock._file.mode == 'r')
|
||||
|
||||
lock.acquire_write()
|
||||
self.assertTrue(lock._reads == 1)
|
||||
self.assertTrue(lock._writes == 1)
|
||||
self.assertTrue(lock._file.mode == 'r+')
|
||||
|
||||
lock.release_write()
|
||||
self.assertTrue(lock._reads == 1)
|
||||
self.assertTrue(lock._writes == 0)
|
||||
self.assertTrue(lock._file.mode == 'r+')
|
||||
|
||||
lock.release_read()
|
||||
self.assertTrue(lock._reads == 0)
|
||||
|
|
Loading…
Reference in a new issue