summaryrefslogtreecommitdiff
path: root/lib/spack/llnl/util/lock.py
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2016-08-09 00:24:54 -0700
committerTodd Gamblin <tgamblin@llnl.gov>2016-08-09 00:24:54 -0700
commit102ac7bcf1bc7fd134b10a9c54e40302d4f1345b (patch)
tree0eeacca085a0bbbe59f4636a79cc5a7377b7d80f /lib/spack/llnl/util/lock.py
parent2042e9a6d85d02adc9424ce6f973e17341ebb292 (diff)
downloadspack-102ac7bcf1bc7fd134b10a9c54e40302d4f1345b.tar.gz
spack-102ac7bcf1bc7fd134b10a9c54e40302d4f1345b.tar.bz2
spack-102ac7bcf1bc7fd134b10a9c54e40302d4f1345b.tar.xz
spack-102ac7bcf1bc7fd134b10a9c54e40302d4f1345b.zip
Move provider cache to home directory and refactor Transactions
Major stuff: - Created a FileCache for managing user cache files in Spack. Currently just handles virtuals. - Moved virtual cache from the repository to the home directory so that users do not need write access to Spack repositories to use them. - Refactored `Transaction` class in `database.py` -- moved it to `LockTransaction` in `lock.py` and made it reusable by other classes. Other additions: - Added tests for file cache and transactions. - Added a few more tests for database - Fixed bug in DB where writes could happen even if exceptions were raised during a transaction. - `spack uninstall` now attempts to repair the database when it discovers that a prefix doesn't exist but a DB record does.
Diffstat (limited to 'lib/spack/llnl/util/lock.py')
-rw-r--r--lib/spack/llnl/util/lock.py69
1 files changed, 67 insertions, 2 deletions
diff --git a/lib/spack/llnl/util/lock.py b/lib/spack/llnl/util/lock.py
index 479a1b0167..e1f5b4878a 100644
--- a/lib/spack/llnl/util/lock.py
+++ b/lib/spack/llnl/util/lock.py
@@ -28,6 +28,9 @@ import errno
import time
import socket
+__all__ = ['Lock', 'LockTransaction', 'WriteTransaction', 'ReadTransaction',
+ 'LockError']
+
# Default timeout in seconds, after which locks will raise exceptions.
_default_timeout = 60
@@ -63,7 +66,9 @@ class Lock(object):
fcntl.lockf(self._fd, op | fcntl.LOCK_NB)
if op == fcntl.LOCK_EX:
- os.write(self._fd, "pid=%s,host=%s" % (os.getpid(), socket.getfqdn()))
+ os.write(
+ self._fd,
+ "pid=%s,host=%s" % (os.getpid(), socket.getfqdn()))
return
except IOError as error:
@@ -170,6 +175,66 @@ class Lock(object):
return False
+class LockTransaction(object):
+ """Simple nested transaction context manager that uses a file lock.
+
+ This class can trigger actions when the lock is acquired for the
+ first time and released for the last.
+
+ If the acquire_fn returns a value, it is used as the return value for
+ __enter__, allowing it to be passed as the `as` argument of a `with`
+ statement.
+
+ If acquire_fn returns a context manager, *its* `__enter__` function will be
+ called in `__enter__` after acquire_fn, and its `__exit__` funciton will be
+ called before `release_fn` in `__exit__`, allowing you to nest a context
+ manager to be used along with the lock.
+
+ Timeout for lock is customizable.
+
+ """
+
+ def __init__(self, lock, acquire_fn=None, release_fn=None,
+ timeout=_default_timeout):
+ self._lock = lock
+ self._timeout = timeout
+ self._acquire_fn = acquire_fn
+ self._release_fn = release_fn
+ self._as = None
+
+ def __enter__(self):
+ if self._enter() and self._acquire_fn:
+ self._as = self._acquire_fn()
+ if hasattr(self._as, '__enter__'):
+ return self._as.__enter__()
+ else:
+ return self._as
+
+ def __exit__(self, type, value, traceback):
+ if self._exit():
+ if self._as and hasattr(self._as, '__exit__'):
+ self._as.__exit__(type, value, traceback)
+ if self._release_fn:
+ self._release_fn(type, value, traceback)
+ if value:
+ raise value
+
+
+class ReadTransaction(LockTransaction):
+ def _enter(self):
+ return self._lock.acquire_read(self._timeout)
+
+ def _exit(self):
+ return self._lock.release_read()
+
+
+class WriteTransaction(LockTransaction):
+ def _enter(self):
+ return self._lock.acquire_write(self._timeout)
+
+ def _exit(self):
+ return self._lock.release_write()
+
+
class LockError(Exception):
"""Raised when an attempt to acquire a lock times out."""
- pass