OLD | NEW |
| (Empty) |
1 # -*- test-case-name: twisted.test.test_lockfile -*- | |
2 # Copyright (c) 2005 Divmod, Inc. | |
3 # See LICENSE for details. | |
4 | |
5 | |
6 """ | |
7 Lock files. | |
8 """ | |
9 | |
10 __metaclass__ = type | |
11 | |
12 import errno, os | |
13 | |
14 from time import time as _uniquefloat | |
15 | |
16 def unique(): | |
17 return str(long(_uniquefloat() * 1000)) | |
18 | |
19 try: | |
20 from os import symlink | |
21 from os import readlink | |
22 from os import remove as rmlink | |
23 from os import rename as mvlink | |
24 except: | |
25 # XXX Implement an atomic thingamajig for win32 | |
26 import shutil | |
27 def symlink(value, filename): | |
28 newlinkname = filename+"."+unique()+'.newlink' | |
29 newvalname = os.path.join(newlinkname,"symlink") | |
30 os.mkdir(newlinkname) | |
31 f = open(newvalname,'wb') | |
32 f.write(value) | |
33 f.flush() | |
34 f.close() | |
35 try: | |
36 os.rename(newlinkname, filename) | |
37 except: | |
38 os.remove(newvalname) | |
39 os.rmdir(newlinkname) | |
40 raise | |
41 | |
42 def readlink(filename): | |
43 return open(os.path.join(filename,'symlink'),'rb').read() | |
44 | |
45 def rmlink(filename): | |
46 shutil.rmtree(filename) | |
47 | |
48 def mvlink(src, dest): | |
49 try: | |
50 shutil.rmtree(dest) | |
51 except: | |
52 pass | |
53 os.rename(src,dest) | |
54 | |
55 | |
56 class FilesystemLock: | |
57 """A mutex. | |
58 | |
59 This relies on the filesystem property that creating | |
60 a symlink is an atomic operation and that it will | |
61 fail if the symlink already exists. Deleting the | |
62 symlink will release the lock. | |
63 | |
64 @ivar name: The name of the file associated with this lock. | |
65 @ivar clean: Indicates whether this lock was released cleanly by its | |
66 last owner. Only meaningful after C{lock} has been called and returns | |
67 True. | |
68 """ | |
69 | |
70 clean = None | |
71 locked = False | |
72 | |
73 def __init__(self, name): | |
74 self.name = name | |
75 | |
76 def lock(self): | |
77 """Acquire this lock. | |
78 | |
79 @rtype: C{bool} | |
80 @return: True if the lock is acquired, false otherwise. | |
81 | |
82 @raise: Any exception os.symlink() may raise, other than | |
83 EEXIST. | |
84 """ | |
85 try: | |
86 pid = readlink(self.name) | |
87 except (OSError, IOError), e: | |
88 if e.errno != errno.ENOENT: | |
89 raise | |
90 self.clean = True | |
91 else: | |
92 if not hasattr(os, 'kill'): | |
93 return False | |
94 try: | |
95 os.kill(int(pid), 0) | |
96 except (OSError, IOError), e: | |
97 if e.errno != errno.ESRCH: | |
98 raise | |
99 rmlink(self.name) | |
100 self.clean = False | |
101 else: | |
102 return False | |
103 | |
104 symlink(str(os.getpid()), self.name) | |
105 self.locked = True | |
106 return True | |
107 | |
108 def unlock(self): | |
109 """Release this lock. | |
110 | |
111 This deletes the directory with the given name. | |
112 | |
113 @raise: Any exception os.readlink() may raise, or | |
114 ValueError if the lock is not owned by this process. | |
115 """ | |
116 pid = readlink(self.name) | |
117 if int(pid) != os.getpid(): | |
118 raise ValueError("Lock %r not owned by this process" % (self.name,)) | |
119 rmlink(self.name) | |
120 self.locked = False | |
121 | |
122 | |
123 def isLocked(name): | |
124 """Determine if the lock of the given name is held or not. | |
125 | |
126 @type name: C{str} | |
127 @param name: The filesystem path to the lock to test | |
128 | |
129 @rtype: C{bool} | |
130 @return: True if the lock is held, False otherwise. | |
131 """ | |
132 l = FilesystemLock(name) | |
133 result = None | |
134 try: | |
135 result = l.lock() | |
136 finally: | |
137 if result: | |
138 l.unlock() | |
139 return not result | |
140 | |
141 | |
142 __all__ = ['FilesystemLock', 'isLocked'] | |
143 | |
OLD | NEW |