I am using python 2.7
I want to create a wrapper function around fcntl.flock() that will timeout after a set interval:
wrapper_function(timeout):
I've tried calling on another thread and using thread.join(timeout) but it seems that fcntl.flock() continues blocking:
def GetLock(self, timeout):
"""Returns true if lock is aquired, false if lock is already in use"""
self.__lock_file = open('proc_lock', 'w')
def GetLockOrTimeOut():
print 'ProcessLock: Acquiring Lock'
fcntl.flock(self.__lock_file.fileno(), fcntl.LOCK_EX)
print 'ProcessLock: Lock Acquired'
thread = threading.Thread(target=GetLockOrTimeOut)
thread.start()
thread.join(timeout)
if thread.isAlive():
print 'GetLock timed out'
return False
else:
return True
I've looked into solutions for terminating threads, the most popular solution seems to be sub-classing threading.thread and adding a feature to raise an exception in the thread. However, I came across a link that says this method will not work with native calls, which I am pretty sure fcntl.flock() is calling a native function. Suggestions?
Context: I am using a file-lock to create a single instance application but I don't want a second instance of the application to sit around and hang until the first instance terminates.
fcntl. flock (fd, operation) ¶ Perform the lock operation operation on file descriptor fd (file objects providing a fileno () method are accepted as well). See the Unix manual flock (2) for details. (On some systems, this function is emulated using fcntl ().)
When fcntl () returns, the structure indicated by the flock pointer is changed to show the first lock that would prevent the proposed lock operation from taking place. The returned structure shows the type of lock that is set, the part of the file that is locked, and the job ID of the job that holds the lock. In the returned structure:
If any other bits in the third argument are set, fcntl () fails with the [EINVAL] error. fcntl () returns 0 if it successfully sets the flags. Sets or clears a file segment lock. You must specify a third argument of type struct flock *. See File Locking for details. fcntl () returns 0 if it successfully clears the lock.
Altering the contents of a CVTSP, or passing in a pointer that does not point to a CVTSP created by the F_GETCVT command, may result in data integrity issues. fcntl () returns 0 if the file position and conversion data was restored. When the F_SETCVT is complete, reading and writing to the file may occur from the file offset provided by the CVTSP.
For Python 3.5+, Glenn Maynard's solution no longer works because of PEP-475. This is a modified version:
import signal, errno
from contextlib import contextmanager
import fcntl
@contextmanager
def timeout(seconds):
def timeout_handler(signum, frame):
# Now that flock retries automatically when interrupted, we need
# an exception to stop it
# This exception will propagate on the main thread, make sure you're calling flock there
raise InterruptedError
original_handler = signal.signal(signal.SIGALRM, timeout_handler)
try:
signal.alarm(seconds)
yield
finally:
signal.alarm(0)
signal.signal(signal.SIGALRM, original_handler)
with timeout(1):
f = open("test.lck", "w")
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
except InterruptedError:
# Catch the exception raised by the handler
# If we weren't raising an exception, flock would automatically retry on signals
print("Lock timed out")
As a complement to @Richard Maw answer above https://stackoverflow.com/a/32580233/17454091 (Don't have enough reputation to post a comment).
In Python 3.2 and newer, for fds to be available in sub-processes one must also provide pass_fds
argument.
Complete solution ends up as:
import subprocess
def flock_with_timeout(fd, timeout, shared=True):
rc = subprocess.call(['flock',
'--shared' if shared else '--exclusive',
'--timeout', str(timeout),
str(fd)],
pass_fds=[fd])
if rc != 0:
raise Exception('Failed to take lock')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With