How can one open a file for exclusive access?
Joshua Muskovitz
joshm at taconic.net
Mon Jan 14 18:06:22 EST 2002
Here's a chunk of code that deals with lockfiles (for preventing multiple
instances of apps, etc) and works on NT and Solaris (I haven't tested it on
other *nixen). Also note this is 1.5.2 code -- some things may break in 2.x
(like the nested import statement). The nice thing about this code is that
if your process dies unexpectedly, the lock is freed automatically by the
OS.
============= cut here ============
class InstanceException(exceptions.Exception):
BAD_MAGIC = 1 # if the lock file exists but does not contain the
right magic
ALREADY_LOCKED = 2 # if the lock file exists
UNSUPPORTED_OS = 3 # if the OS just can't cope
def __init__(self, errno, msg):
self.errno = errno
self.msg = msg
self.args = msg
class SingleInstance:
def __init__(self, filename, magic = "SingleInstance Magic File --
Shh!"):
# store the filename
self.filename = filename
self.fd = None # Note: This has different meaning under NT and
Posix
self.magic = magic
self.CreateLock()
def CreateLock(self):
# if we are already locked, then just return
if self.fd != None:
return
# if the file exists, then make sure it is a special
# SingleInstance file by checking the first line for
# the magic string
try:
fd = os.open(self.filename, os.O_EXCL|os.O_RDONLY)
magic = os.read(fd, len(self.magic))
os.close(fd)
if magic != self.magic:
raise InstanceException, (InstanceException.BAD_MAGIC, "File
exists and contains bad magic!")
except exceptions.OSError, e:
# the only legal error here is a "No such file or directory"
if e.errno == 2: # No such file or directory
pass
else:
raise e
if os.name == 'nt':
###########################################################
#
# NT SPECIFIC LOCK ACQUISITION METHOD
# Try to delete the lock file.
# This will fail if another process has the lock.
try:
os.remove(self.filename)
except exceptions.OSError, e:
if e.errno == 2: # No such file or directory
pass
elif e.errno == 13: # Permission denied
raise InstanceException,
(InstanceException.ALREADY_LOCKED, "Another process owns this lock!")
# Next, try to create the file with an exclusive lock.
# This will fail if another process has the lock.
try:
self.fd = os.open(self.filename,
os.O_CREAT|os.O_EXCL|os.O_WRONLY)
os.write(self.fd, self.magic)
except exceptions.OSError, e:
raise InstanceException, (InstanceException.ALREADY_LOCKED,
"Unable to create lock!")
# END OF NT SPECIFIC LOCK ACQUISITION METHOD
#
###########################################################
elif os.name == 'posix':
###########################################################
#
# POSIX SPECIFIC LOCK ACQUISITION METHOD
# Don't delete the lock file.
# Try to create the file. This *should* always succeed.
try:
self.fd = open(self.filename, 'w')
except:
raise InstanceException, (InstanceException.ALREADY_LOCKED,
"Unable to create lock!")
# Attempt to lock the file. This will fail if another
# process already has the lock.
try:
import fcntl
fcntl.flock(self.fd.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB)
except exceptions.IOError, e:
# another process has the lock, but we've just trashed
# the file. write the magic and close it!
self.fd.write(self.magic)
self.fd.close()
self.fd = None
raise InstanceException, (InstanceException.ALREADY_LOCKED,
"Another process owns this lock!")
# Write the magic to the file
try:
self.fd.write(self.magic)
self.fd.flush()
except:
raise InstanceException, (InstanceException.ALREADY_LOCKED,
"Unable to create lock!")
# END OF POSIX SPECIFIC LOCK ACQUISITION METHOD
#
###########################################################
else:
###########################################################
#
# Everyone else
raise InstanceException, (InstanceException.UNSUPPORTED_OS,
"This OS isn't supported!")
#
###########################################################
def FreeLock(self):
if self.fd != None:
# if there is a lockfile open, then close it
# note that self.fd holds different things based on the OS
if os.name == 'nt':
os.close(self.fd)
elif os.name == 'posix':
self.fd.close()
else:
raise InstanceException, (InstanceException.UNSUPPORTED_OS,
"This OS isn't supported!")
self.fd = None
os.remove(self.filename)
def TouchLock(self):
if self.fd != None:
# if there is a lockfile open, touch it
# this works on all OSes.
now = time.time()
os.utime(self.filename, (now, now))
def __del__(self):
self.FreeLock()
============= end of code ============
And here's how to use it...
try:
filename = *** PLACE YOUR FILENAME HERE ***
ourLock = SingleInstance(filename)
except InstanceException, e:
if e.errno == InstanceException.ALREADY_LOCKED:
*** HANDLE YOUR EXCEPTION HERE ***
...
ourLock.TouchLock() # this updates the timestamp on the lockfile,
# which may be useful to you (to allow other processes to see that
# you are still alive, for example)
...
ourLock.FreeLock() # frees the lock, and deletes the file
Enjoy.
-- josh
-----= Posted via Newsfeeds.Com, Uncensored Usenet News =-----
http://www.newsfeeds.com - The #1 Newsgroup Service in the World!
-----== Over 80,000 Newsgroups - 16 Different Servers! =-----
More information about the Python-list
mailing list