summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd Gamblin <tgamblin@llnl.gov>2016-10-04 15:36:37 -0700
committerGitHub <noreply@github.com>2016-10-04 15:36:37 -0700
commitbff1656a1a842b4de217129cc8cba732a30928a7 (patch)
treefdfcae078688c317256f1135ad60967e794d61da
parent9daafc32f127b0ce22dba40cbdfc8c139c2acf58 (diff)
downloadspack-bff1656a1a842b4de217129cc8cba732a30928a7.tar.gz
spack-bff1656a1a842b4de217129cc8cba732a30928a7.tar.bz2
spack-bff1656a1a842b4de217129cc8cba732a30928a7.tar.xz
spack-bff1656a1a842b4de217129cc8cba732a30928a7.zip
Read-only locks should close fd before opening for write. (#1906)
- Fixes bad file descriptor error in lock acquire, #1904 - Fix bug introduced in previous PR #1857 - Backported fix from soon-to-be merged fine-grained DB locking branch.
-rw-r--r--lib/spack/llnl/util/lock.py8
-rw-r--r--lib/spack/spack/test/lock.py29
2 files changed, 37 insertions, 0 deletions
diff --git a/lib/spack/llnl/util/lock.py b/lib/spack/llnl/util/lock.py
index b5e3a3a8f8..f5f53101ae 100644
--- a/lib/spack/llnl/util/lock.py
+++ b/lib/spack/llnl/util/lock.py
@@ -69,6 +69,14 @@ class Lock(object):
start_time = time.time()
while (time.time() - start_time) < timeout:
try:
+ # If this is already open read-only and we want to
+ # upgrade to an exclusive write lock, close first.
+ if self._fd is not None:
+ flags = fcntl.fcntl(self._fd, fcntl.F_GETFL)
+ if op == fcntl.LOCK_EX and flags | os.O_RDONLY:
+ os.close(self._fd)
+ self._fd = None
+
if self._fd is None:
mode = os.O_RDWR if op == fcntl.LOCK_EX else os.O_RDONLY
self._fd = os.open(self._file_path, mode)
diff --git a/lib/spack/spack/test/lock.py b/lib/spack/spack/test/lock.py
index fb96539897..32cbe13ce1 100644
--- a/lib/spack/spack/test/lock.py
+++ b/lib/spack/spack/test/lock.py
@@ -158,6 +158,35 @@ class LockTest(unittest.TestCase):
self.timeout_write, self.timeout_write)
#
+ # Test that read can be upgraded to write.
+ #
+ def test_upgrade_read_to_write(self):
+ # ensure lock file exists the first time, so we open it read-only
+ # to begin wtih.
+ touch(self.lock_path)
+
+ lock = Lock(self.lock_path)
+ self.assertTrue(lock._reads == 0)
+ self.assertTrue(lock._writes == 0)
+
+ lock.acquire_read()
+ self.assertTrue(lock._reads == 1)
+ self.assertTrue(lock._writes == 0)
+
+ lock.acquire_write()
+ self.assertTrue(lock._reads == 1)
+ self.assertTrue(lock._writes == 1)
+
+ lock.release_write()
+ self.assertTrue(lock._reads == 1)
+ self.assertTrue(lock._writes == 0)
+
+ lock.release_read()
+ self.assertTrue(lock._reads == 0)
+ self.assertTrue(lock._writes == 0)
+ self.assertTrue(lock._fd is None)
+
+ #
# Longer test case that ensures locks are reusable. Ordering is
# enforced by barriers throughout -- steps are shown with numbers.
#