Follow-up: Win32/Posix file locking

Sheila King sheila at spamcop.net
Thu Aug 30 01:31:38 CEST 2001


OK, to follow up the other two threads that I've recently started on the
topic of cross-platform file locking, I will share the code that I am
currently working with. I believe that I have solved my problem. I've
tested the code on both Win98 and Linux and it seems to work.

Ignacio suggested that I rename the module to something having to do
with Mutex objects, rather than file locking, and I have done so. The
idea is to acquire a lock on some Mutex before trying to write to a
datafile.

Here is the parent class:

-----------------------(begin MutexFile.py)--------------------------
'''MutexFile.py

wraps WinMutexFile.py and posixMutexFile.py to provide
a single interface for working with mutex files
across win32 and posix platforms.
Does not support fine-grained locking.
Recommended for getting a lock on a sentinel file
before trying to read/write data to a separate data file.
'''
import os

if os.name == 'nt':
     from winMutexFile import mutexfile
elif os.name == 'posix':
     from posixMutexFile import mutexfile
else:
    raise ImportError, "MutexFile is not supported on your platform."


class MutexFile(mutexfile):
    pass
-----------------------(end MutexFile.py)--------------------------


Below I present the two platform specific modules that implement the
file locking. I've used non-blocking locks with retries and time outs.
I've tried to handle a process crashing by coding a __del___ function
for the object. If anyone has feedback, I'm more than interested in
hearing it.

--
Sheila King
http://www.thinkspot.net/sheila/
http://www.k12groups.org/

----------------------(begin posixMutexFile.py)----------------------
''' posixMutexFile.py

class lockfile
supports the same function calls as
winLock.lockfile
export to LockFile.py: a wrapper module
around the win and posix mutexfiles
'''

import os, fcntl
from time import time

MAXTIME = 8  # number of secs to retry for a lock before timing out

class mutexfile:
    def __init__(self, filename):
        if os.access(filename, os.F_OK):
            self.filename = filename
        else:
            errmssg = filename + \
				" does not exist. Can't lock non-existent file."
            raise IOError, errmssg

    def __del__(self):
        try:
            self.unlock()
        except:
            pass
        try:
            self.f.close()
        except:
            pass
        if hasattr(self, 'fd'):
            del self.fd

    def getReadLock(self):
        start_time = time()
        while time() - start_time < MAXTIME:
            try:
                self.f = open(self.filename, 'r')
                self.fd = self.f.fileno()
                fcntl.lockf(self.fd, fcntl.LOCK_SH | fcntl.LOCK_NB)
                return 1
            except:
                self.f.close()
                del self.fd
        if not hasattr(self, 'fd'):
            errmssg = self.filename + " temporarily unavailable"
            raise IOError, errmssg

    def getWriteLock(self):
        start_time = time()
        while time() - start_time < MAXTIME:
            try:
                self.f = open(self.filename, 'r+')
                self.fd = self.f.fileno()
                fcntl.lockf(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
                return 1
            except:
                self.f.close()
                del self.fd
        if not hasattr(self, 'fd'):
            errmssg = self.filename + " temporarily unavailable"
            raise IOError, errmssg

    def unlock(self):
        fcntl.lockf(self.fd, fcntl.LOCK_UN)
        self.f.close()
        del self.fd

    def flock(self, flag):
        '''flags are:
        LOCK_UN - unlock
        LOCK_SH - acquire a shared (or read) lock
        LOCK_EX - acquire an exclusive (or write) lock
        '''
        if flag == 'LOCK_SH':
            self.getReadLock()
        elif flag == 'LOCK_EX':
            self.getWriteLock()
        elif flag == 'LOCK_UN':
            self.unlock()
        else:
            errmssg = "The flag " + flag + \
				" is not implemented for flock"
            raise NotImplementedError, errmssg
----------------------(end posixMutexFile.py)----------------------

----------------------(begin winMutexFile.py)----------------------
''' winMutexFile.py

class lockfile
supports the same function calls as
posixLock.lockobject
export to lockobject.py: a wrapper module
around the win and posix lockobjects
'''
import os
from time import time

try:
    import win32file
    from win32con import GENERIC_READ, GENERIC_WRITE,\
        FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL
except ImportError, e:
    print e
    print "winMutexFile.py requires ActiveState.com Python win32
extensions"

MAXTIME = 8  # number of secs to retry for a lock before timing out

class mutexfile:
    def __init__(self, filename):
        if os.access(filename, os.F_OK):
            self.filename = filename
        else:
            errmssg = filename + \
				" does not exist. Can't lock non-existent file."
            raise IOError, errmssg

    def __del__(self):
        try:
            self.unlock()
        except:
            pass
        try:
            self.f.close()
        except:
            pass
        if hasattr(self, 'fd'):
            del self.fd

    def getReadLock(self):
        start_time = time()
        while time() - start_time < MAXTIME:
            try:
                self.fd = win32file.CreateFile(self.filename,\
											  GENERIC_READ,\
                                              FILE_SHARE_READ, None,\
											  OPEN_EXISTING,\
                                              FILE_ATTRIBUTE_NORMAL, 0)
                return 1
            except:
                pass
        if not hasattr(self, 'fd'):
            errmssg = self.filename + " temporarily unavailable"
            raise IOError, errmssg

    def getWriteLock(self):
        start_time = time()
        while time() - start_time < MAXTIME:
            try:
                self.fd = win32file.CreateFile(self.filename,\
											  GENERIC_READ,\
                                              0, None, OPEN_EXISTING,\
                                              FILE_ATTRIBUTE_NORMAL, 0)
                return 1
            except:
                pass
        if not hasattr(self,'fd'):
            errmssg = self.filename + " temporarily unavailable"
            raise IOError, errmssg

    def unlock(self):
        win32file.CloseHandle(self.fd)
        del self.fd

    def flock(self, flag):
        '''flags are:
        LOCK_UN - unlock
        LOCK_SH - acquire a shared (or read) lock
        LOCK_EX - acquire an exclusive (or write) lock
        '''
        if flag == 'LOCK_SH':
            self.getReadLock()
        elif flag == 'LOCK_EX':
            self.getWriteLock()
        elif flag == 'LOCK_UN':
            self.unlock()
        else:
            errmssg = "The flag " + flag + \
				" is not implemented for flock"
            raise NotImplementedError, errmssg

----------------------(end winMutexFile.py)----------------------



More information about the Python-list mailing list