diff options
Diffstat (limited to 'lib/spack/llnl/util/lock.py')
-rw-r--r-- | lib/spack/llnl/util/lock.py | 59 |
1 files changed, 42 insertions, 17 deletions
diff --git a/lib/spack/llnl/util/lock.py b/lib/spack/llnl/util/lock.py index 3b93303297..37989bac35 100644 --- a/lib/spack/llnl/util/lock.py +++ b/lib/spack/llnl/util/lock.py @@ -4,9 +4,9 @@ # SPDX-License-Identifier: (Apache-2.0 OR MIT) import errno -import fcntl import os import socket +import sys import time from datetime import datetime from typing import Dict, Tuple # novm @@ -15,6 +15,10 @@ import llnl.util.tty as tty import spack.util.string +if sys.platform != 'win32': + import fcntl + + __all__ = [ 'Lock', 'LockDowngradeError', @@ -29,8 +33,6 @@ __all__ = [ 'CantCreateLockError' ] -#: Mapping of supported locks to description -lock_type = {fcntl.LOCK_SH: 'read', fcntl.LOCK_EX: 'write'} #: A useful replacement for functions that should return True when not provided #: for example. @@ -166,6 +168,29 @@ def _attempts_str(wait_time, nattempts): return ' after {0:0.2f}s and {1}'.format(wait_time, attempts) +class LockType(object): + READ = 0 + WRITE = 1 + + @staticmethod + def to_str(tid): + ret = "READ" + if tid == LockType.WRITE: + ret = "WRITE" + return ret + + @staticmethod + def to_module(tid): + lock = fcntl.LOCK_SH + if tid == LockType.WRITE: + lock = fcntl.LOCK_EX + return lock + + @staticmethod + def is_valid(op): + return op == LockType.READ \ + or op == LockType.WRITE + class Lock(object): """This is an implementation of a filesystem lock using Python's lockf. @@ -276,9 +301,10 @@ class Lock(object): successfully acquired, the total wait time and the number of attempts is returned. """ - assert op in lock_type + assert LockType.is_valid(op) + op_str = LockType.to_str(op) - self._log_acquiring('{0} LOCK'.format(lock_type[op].upper())) + self._log_acquiring('{0} LOCK'.format(op_str)) timeout = timeout or self.default_timeout # Create file and parent directories if they don't exist. @@ -286,13 +312,13 @@ class Lock(object): self._ensure_parent_directory() self._file = file_tracker.get_fh(self.path) - if op == fcntl.LOCK_EX and self._file.mode == 'r': + if LockType.to_module(op) == fcntl.LOCK_EX and self._file.mode == 'r': # Attempt to upgrade to write lock w/a read-only file. # If the file were writable, we'd have opened it 'r+' raise LockROFileError(self.path) self._log_debug("{0} locking [{1}:{2}]: timeout {3} sec" - .format(lock_type[op], self._start, self._length, + .format(op_str.lower(), self._start, self._length, timeout)) poll_intervals = iter(Lock._poll_interval_generator()) @@ -313,17 +339,16 @@ class Lock(object): return total_wait_time, num_attempts raise LockTimeoutError("Timed out waiting for a {0} lock." - .format(lock_type[op])) + .format(op_str.lower())) def _poll_lock(self, op): """Attempt to acquire the lock in a non-blocking manner. Return whether the locking attempt succeeds """ - assert op in lock_type - + module_op = LockType.to_module(op) try: # Try to get the lock (will raise if not available.) - fcntl.lockf(self._file, op | fcntl.LOCK_NB, + fcntl.lockf(self._file, module_op | fcntl.LOCK_NB, self._length, self._start, os.SEEK_SET) # help for debugging distributed locking @@ -331,11 +356,11 @@ class Lock(object): # All locks read the owner PID and host self._read_log_debug_data() self._log_debug('{0} locked {1} [{2}:{3}] (owner={4})' - .format(lock_type[op], self.path, + .format(LockType.to_str(op), self.path, self._start, self._length, self.pid)) # Exclusive locks write their PID/host - if op == fcntl.LOCK_EX: + if module_op == fcntl.LOCK_EX: self._write_log_debug_data() return True @@ -420,7 +445,7 @@ class Lock(object): if self._reads == 0 and self._writes == 0: # can raise LockError. - wait_time, nattempts = self._lock(fcntl.LOCK_SH, timeout=timeout) + wait_time, nattempts = self._lock(LockType.READ, timeout=timeout) self._reads += 1 # Log if acquired, which includes counts when verbose self._log_acquired('READ LOCK', wait_time, nattempts) @@ -445,7 +470,7 @@ class Lock(object): if self._writes == 0: # can raise LockError. - wait_time, nattempts = self._lock(fcntl.LOCK_EX, timeout=timeout) + wait_time, nattempts = self._lock(LockType.WRITE, timeout=timeout) self._writes += 1 # Log if acquired, which includes counts when verbose self._log_acquired('WRITE LOCK', wait_time, nattempts) @@ -489,7 +514,7 @@ class Lock(object): if self._writes == 1 and self._reads == 0: self._log_downgrading() # can raise LockError. - wait_time, nattempts = self._lock(fcntl.LOCK_SH, timeout=timeout) + wait_time, nattempts = self._lock(LockType.READ, timeout=timeout) self._reads = 1 self._writes = 0 self._log_downgraded(wait_time, nattempts) @@ -508,7 +533,7 @@ class Lock(object): if self._reads == 1 and self._writes == 0: self._log_upgrading() # can raise LockError. - wait_time, nattempts = self._lock(fcntl.LOCK_EX, timeout=timeout) + wait_time, nattempts = self._lock(LockType.WRITE, timeout=timeout) self._reads = 0 self._writes = 1 self._log_upgraded(wait_time, nattempts) |