1 # -*- coding: utf-8 -*-
3 # Code from http://ender.snowburst.org:4747/~jjohns/interlocks.py
7 _
= L10n
.get_translation()
16 InterProcessLock
= None
19 Just use me like a thread lock. acquire() / release() / locked()
21 Differences compared to thread locks:
22 1. By default, acquire()'s wait parameter is false.
23 2. When acquire fails, SingleInstanceError is thrown instead of simply returning false.
24 3. acquire() can take a 3rd parameter retry_time, which, if wait is True, tells the locking
25 mechanism how long to sleep between retrying the lock. Has no effect for unix/InterProcessLockFcntl.
27 Differences in fpdb version to JJ's original:
28 1. Changed acquire() to return false like other locks
29 2. Made acquire fail if same process already has the lock
32 class SingleInstanceError(RuntimeError):
33 "Thrown when you try to acquire an InterProcessLock and another version of the process is already running."
35 class InterProcessLockBase
:
36 def __init__(self
, name
=None ):
37 self
._has
_lock
= False
43 def getHashedName(self
):
44 return base64
.b64encode(self
.name
).replace('=','')
46 def acquire_impl(self
, wait
): abstract
48 def acquire(self
, source
, wait
=False, retry_time
=1):
51 if self
._has
_lock
: # make sure 2nd acquire in same process fails
52 print _("lock already held by:"),self
.heldBy
54 while not self
._has
_lock
:
56 self
.acquire_impl(wait
)
59 #print 'i have the lock'
60 except SingleInstanceError
:
62 # raise # change back to normal acquire functionality, sorry JJ!
64 time
.sleep(retry_time
)
69 self
._has
_lock
= False
78 LOCK_FILE_DIRECTORY
= '/tmp'
80 class InterProcessLockFcntl(InterProcessLockBase
):
81 def __init__(self
, name
=None):
82 InterProcessLockBase
.__init
__(self
, name
)
84 self
.lock_file_name
= os
.path
.join(LOCK_FILE_DIRECTORY
, self
.getHashedName() + '.lck')
85 assert(os
.path
.isdir(LOCK_FILE_DIRECTORY
))
87 # This is the suggested way to get a safe file name, but I like having a descriptively named lock file.
88 def getHashedName(self
):
90 bad_filename_character_re
= re
.compile(r
'/\?<>\\\:;\*\|\'\"\^
=\
.\
[\
]')
91 return bad_filename_character_re.sub('_
',self.name)
93 def acquire_impl(self, wait):
94 self.lockfd = open(self.lock_file_name, 'w
')
95 fcntrl_options = fcntl.LOCK_EX
97 fcntrl_options |= fcntl.LOCK_NB
99 fcntl.flock(self.lockfd, fcntrl_options)
103 raise SingleInstanceError('Could
not acquire exclusive lock on
'+self.lock_file_name)
105 def release_impl(self):
106 fcntl.lockf(self.lockfd, fcntl.LOCK_UN)
110 os.unlink(self.lock_file_name)
112 # We don't care about the existence of the
file too much here
. It
's the flock() we care about,
113 # And that should just go away magically.
116 class InterProcessLockWin32(InterProcessLockBase):
117 def __init__(self, name=None):
118 InterProcessLockBase.__init__(self, name)
121 def acquire_impl(self,wait):
122 self.mutex = win32event.CreateMutex(None, 0, self.getHashedName())
123 if win32api.GetLastError() == winerror.ERROR_ALREADY_EXISTS:
126 raise SingleInstanceError('Could
not acquire exclusive lock on
' + self.name)
128 def release_impl(self):
131 class InterProcessLockSocket(InterProcessLockBase):
132 def __init__(self, name=None):
133 InterProcessLockBase.__init__(self, name)
135 self.portno = 65530 - abs(self.getHashedName().__hash__()) % 32749
137 def acquire_impl(self, wait):
138 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
140 self.socket.bind(('127.0.0.1', self.portno))
144 raise SingleInstanceError('Could
not acquire exclusive lock on
' + self.name)
146 def release_impl(self):
150 # Set InterProcessLock to the correct type given the sysem parameters available
153 InterProcessLock = InterProcessLockFcntl
159 InterProcessLock = InterProcessLockWin32
162 InterProcessLock = InterProcessLockSocket
164 def test_construct():
166 # Making the name of the test unique so it can be executed my multiple users on the same machine.
167 >>> test_name = 'InterProcessLockTest
' +str(os.getpid()) + str(time.time())
169 >>> lock1 = InterProcessLock(name=test_name)
173 >>> lock2 = InterProcessLock(name=test_name)
174 >>> lock3 = InterProcessLock(name=test_name)
176 # Since lock1 is locked, other attempts to acquire it fail.
183 # Release the lock and let lock2 have it.
191 # Release it and give it back to lock1
216 >>> if os.name == 'posix
':
217 ... def os_independent_kill(pid):
219 ... os.kill(pid, signal.SIGKILL)
221 ... assert(os.name == 'nt
')
222 ... def os_independent_kill(pid):
223 ... ''' http://www.python.org/doc/faq/windows/#how-do-i-emulate-os-kill-in-windows '''
226 ... import pywintypes
227 ... handle = win32api.OpenProcess(win32con.PROCESS_TERMINATE , pywintypes.FALSE, pid)
228 ... #return (0 != win32api.TerminateProcess(handle, 0))
230 # Test to acquire the lock in another process.
231 >>> def execute(cmd):
232 ... cmd = 'import time
;' + cmd + 'time
.sleep(10);'
233 ... process = subprocess.Popen([sys.executable, '-c
', cmd])
234 ... pid = process.pid
235 ... time.sleep(2) # quick hack, but we test synchronization in the end
238 >>> pid = execute('import interlocks
;a
=interlocks
.InterProcessLock(name
=\\''+test_name
+ '\\');a
.acquire();')
243 >>> os_independent_kill(pid)
253 >>> pid = execute('import interlocks
;a
=interlocks
.InterProcessLock(name
=\\''+test_name
+ '\\');a
.acquire();')
258 >>> os_independent_kill(pid)
260 >>> lock1.acquire(True)
268 if __name__=='__main__
':
270 doctest.testmod(optionflags=doctest.IGNORE_EXCEPTION_DETAIL)