diff options
author | Todd Gamblin <tgamblin@llnl.gov> | 2016-08-09 00:24:54 -0700 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2016-08-09 00:24:54 -0700 |
commit | 102ac7bcf1bc7fd134b10a9c54e40302d4f1345b (patch) | |
tree | 0eeacca085a0bbbe59f4636a79cc5a7377b7d80f /lib/spack/llnl/util/lock.py | |
parent | 2042e9a6d85d02adc9424ce6f973e17341ebb292 (diff) | |
download | spack-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.py | 69 |
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 |