1 # -*- test-case-name: twisted.test.test_lockfile -*-
2 # Copyright (c) 2005 Divmod, Inc.
3 # Copyright (c) Twisted Matrix Laboratories.
4 # See LICENSE for details.
7 Filesystem-based interprocess mutex.
14 from time import time as _uniquefloat
16 from twisted.python.runtime import platform
19 return str(long(_uniquefloat() * 1000))
22 if not platform.isWindows():
24 from os import symlink
25 from os import readlink
26 from os import remove as rmlink
32 from win32api import OpenProcess
37 ERROR_ACCESS_DENIED = 5
38 ERROR_INVALID_PARAMETER = 87
40 def kill(pid, signal):
42 OpenProcess(0, 0, pid)
43 except pywintypes.error, e:
44 if e.args[0] == ERROR_ACCESS_DENIED:
46 elif e.args[0] == ERROR_INVALID_PARAMETER:
47 raise OSError(errno.ESRCH, None)
50 raise RuntimeError("OpenProcess is required to fail.")
54 # XXX Implement an atomic thingamajig for win32
55 def symlink(value, filename):
56 newlinkname = filename+"."+unique()+'.newlink'
57 newvalname = os.path.join(newlinkname,"symlink")
59 f = _open(newvalname,'wcb')
64 rename(newlinkname, filename)
70 def readlink(filename):
72 fObj = _open(os.path.join(filename,'symlink'), 'rb')
74 if e.errno == errno.ENOENT or e.errno == errno.EIO:
75 raise OSError(e.errno, None)
83 os.remove(os.path.join(filename, 'symlink'))
92 This relies on the filesystem property that creating
93 a symlink is an atomic operation and that it will
94 fail if the symlink already exists. Deleting the
95 symlink will release the lock.
97 @ivar name: The name of the file associated with this lock.
99 @ivar clean: Indicates whether this lock was released cleanly by its
100 last owner. Only meaningful after C{lock} has been called and
103 @ivar locked: Indicates whether the lock is currently held by this
110 def __init__(self, name):
119 @return: True if the lock is acquired, false otherwise.
121 @raise: Any exception os.symlink() may raise, other than
127 symlink(str(os.getpid()), self.name)
129 if _windows and e.errno in (errno.EACCES, errno.EIO):
130 # The lock is in the middle of being deleted because we're
131 # on Windows where lock removal isn't atomic. Give up, we
132 # don't know how long this is going to take.
134 if e.errno == errno.EEXIST:
136 pid = readlink(self.name)
138 if e.errno == errno.ENOENT:
139 # The lock has vanished, try to claim it in the
140 # next iteration through the loop.
144 if _windows and e.errno == errno.EACCES:
145 # The lock is in the middle of being
146 # deleted because we're on Windows where
147 # lock removal isn't atomic. Give up, we
148 # don't know how long this is going to
156 if e.errno == errno.ESRCH:
157 # The owner has vanished, try to claim it in the next
158 # iteration through the loop.
162 if e.errno == errno.ENOENT:
163 # Another process cleaned up the lock.
164 # Race them to acquire it in the next
165 # iteration through the loop.
182 This deletes the directory with the given name.
184 @raise: Any exception os.readlink() may raise, or
185 ValueError if the lock is not owned by this process.
187 pid = readlink(self.name)
188 if int(pid) != os.getpid():
189 raise ValueError("Lock %r not owned by this process" % (self.name,))
195 """Determine if the lock of the given name is held or not.
198 @param name: The filesystem path to the lock to test
201 @return: True if the lock is held, False otherwise.
203 l = FilesystemLock(name)
213 __all__ = ['FilesystemLock', 'isLocked']