summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorskip.montanaro <skip.montanaro@gmail.com>2010-11-06 21:25:25 +0000
committerskip.montanaro <skip.montanaro@gmail.com>2010-11-06 21:25:25 +0000
commit2065cfa86adab2e0458607391d6ec44a75d42f93 (patch)
tree414c583b6a2a1ba2a1726d547086c0b602db9067
parentc5019dd6073623582465ec6c10f8118f07b306aa (diff)
downloadlockfile-2065cfa86adab2e0458607391d6ec44a75d42f93.tar.gz
add symlinklockfile module
-rw-r--r--lockfile/symlinklockfile.py66
-rw-r--r--test/test_lockfile.py8
2 files changed, 73 insertions, 1 deletions
diff --git a/lockfile/symlinklockfile.py b/lockfile/symlinklockfile.py
new file mode 100644
index 0000000..0447e22
--- /dev/null
+++ b/lockfile/symlinklockfile.py
@@ -0,0 +1,66 @@
+from __future__ import absolute_import
+
+import time
+import os
+
+from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
+ AlreadyLocked)
+
+class SymlinkLockFile(LockBase):
+ """Lock access to a file using symlink(2)."""
+
+ def __init__(self, path, threaded=True):
+ # super(SymlinkLockFile).__init(...)
+ LockBase.__init__(self, path, threaded)
+ # split it back!
+ self.unique_name = os.path.split(self.unique_name)[1]
+
+ def acquire(self, timeout=None):
+ # Hopefully unnecessary for symlink.
+ #try:
+ # open(self.unique_name, "wb").close()
+ #except IOError:
+ # raise LockFailed("failed to create %s" % self.unique_name)
+
+ end_time = time.time()
+ if timeout is not None and timeout > 0:
+ end_time += timeout
+
+ while True:
+ # Try and create a hard link to it.
+ try:
+ os.symlink(self.unique_name, self.lock_file)
+ except OSError:
+ # Link creation failed. Maybe we've double-locked?
+ if self.i_am_locking():
+ # Linked to out unique name. Proceed.
+ return
+ else:
+ # Otherwise the lock creation failed.
+ if timeout is not None and time.time() > end_time:
+ if timeout > 0:
+ raise LockTimeout
+ else:
+ raise AlreadyLocked
+ time.sleep(timeout/10 if timeout is not None else 0.1)
+ else:
+ # Link creation succeeded. We're good to go.
+ return
+
+ def release(self):
+ if not self.is_locked():
+ raise NotLocked
+ elif not self.i_am_locking():
+ raise NotMyLock
+ os.unlink(self.lock_file)
+
+ def is_locked(self):
+ return os.path.islink(self.lock_file)
+
+ def i_am_locking(self):
+ return os.path.islink(self.lock_file) and \
+ os.readlink(self.lock_file) == self.unique_name
+
+ def break_lock(self):
+ if os.path.islink(self.lock_file): # exists && link
+ os.unlink(self.lock_file)
diff --git a/test/test_lockfile.py b/test/test_lockfile.py
index 3b70cdd..e1f4f72 100644
--- a/test/test_lockfile.py
+++ b/test/test_lockfile.py
@@ -1,12 +1,18 @@
import sys
-import lockfile.linklockfile, lockfile.mkdirlockfile, lockfile.pidlockfile
+import lockfile.linklockfile
+import lockfile.mkdirlockfile
+import lockfile.pidlockfile
+import lockfile.symlinklockfile
from compliancetest import ComplianceTest
class TestLinkLockFile(ComplianceTest):
class_to_test = lockfile.linklockfile.LinkLockFile
+class TestSymlinkLockFile(ComplianceTest):
+ class_to_test = lockfile.symlinklockfile.SymlinkLockFile
+
class TestMkdirLockFile(ComplianceTest):
class_to_test = lockfile.mkdirlockfile.MkdirLockFile