33
|
1 from __future__ import absolute_import
|
|
2
|
|
3 import time
|
|
4 import os
|
|
5
|
|
6 from . import (LockBase, LockFailed, NotLocked, NotMyLock, LockTimeout,
|
|
7 AlreadyLocked)
|
|
8
|
|
9 class LinkLockFile(LockBase):
|
|
10 """Lock access to a file using atomic property of link(2).
|
|
11
|
|
12 >>> lock = LinkLockFile('somefile')
|
|
13 >>> lock = LinkLockFile('somefile', threaded=False)
|
|
14 """
|
|
15
|
|
16 def acquire(self, timeout=None):
|
|
17 try:
|
|
18 open(self.unique_name, "wb").close()
|
|
19 except IOError:
|
|
20 raise LockFailed("failed to create %s" % self.unique_name)
|
|
21
|
|
22 timeout = timeout is not None and timeout or self.timeout
|
|
23 end_time = time.time()
|
|
24 if timeout is not None and timeout > 0:
|
|
25 end_time += timeout
|
|
26
|
|
27 while True:
|
|
28 # Try and create a hard link to it.
|
|
29 try:
|
|
30 os.link(self.unique_name, self.lock_file)
|
|
31 except OSError:
|
|
32 # Link creation failed. Maybe we've double-locked?
|
|
33 nlinks = os.stat(self.unique_name).st_nlink
|
|
34 if nlinks == 2:
|
|
35 # The original link plus the one I created == 2. We're
|
|
36 # good to go.
|
|
37 return
|
|
38 else:
|
|
39 # Otherwise the lock creation failed.
|
|
40 if timeout is not None and time.time() > end_time:
|
|
41 os.unlink(self.unique_name)
|
|
42 if timeout > 0:
|
|
43 raise LockTimeout("Timeout waiting to acquire"
|
|
44 " lock for %s" %
|
|
45 self.path)
|
|
46 else:
|
|
47 raise AlreadyLocked("%s is already locked" %
|
|
48 self.path)
|
|
49 time.sleep(timeout is not None and timeout/10 or 0.1)
|
|
50 else:
|
|
51 # Link creation succeeded. We're good to go.
|
|
52 return
|
|
53
|
|
54 def release(self):
|
|
55 if not self.is_locked():
|
|
56 raise NotLocked("%s is not locked" % self.path)
|
|
57 elif not os.path.exists(self.unique_name):
|
|
58 raise NotMyLock("%s is locked, but not by me" % self.path)
|
|
59 os.unlink(self.unique_name)
|
|
60 os.unlink(self.lock_file)
|
|
61
|
|
62 def is_locked(self):
|
|
63 return os.path.exists(self.lock_file)
|
|
64
|
|
65 def i_am_locking(self):
|
|
66 return (self.is_locked() and
|
|
67 os.path.exists(self.unique_name) and
|
|
68 os.stat(self.unique_name).st_nlink == 2)
|
|
69
|
|
70 def break_lock(self):
|
|
71 if os.path.exists(self.lock_file):
|
|
72 os.unlink(self.lock_file)
|
|
73
|