Windows NT Files -- Locking

Python's win32 access for file locking -- flock style


The need for file locking tends arise every so often. Some people may be used to flock style locking, which has 4 basic cases: shared,exclusive,blocking and non-blocking. Shared locking is typically used when multiple people want to read a file. Exclusive is for writing. Blocking means that the process will wait until it is able to lock the file. Non-blocking will return immediately and tell you the lock failed. In win32 the standard CreateFile api gives you the ability to do exclusive or shared locking. However, what it does not give you is the ability to switch between blocking/non-blocking (it fails immediately -- does not block) To do that, you need to use LockfileEx -- which can even lock a specific part of a file.


The basic procedure for doing this is to first call Createfile to give you a filehandle. Then call LockfileEx with the filehandle. Do whatever to the file. Call UnlockfileEx. Then close the filehandle. (Some of you may want to close the filehandle to kill the locks, it doesn't work that way with win32, at least according to the msdn)

Below is a class called Flock, which gives you exclusive/shared locking with non-blocking/blocking abilities. If you can think of any optimizations or changes, be sure to let me know.

CreateFile provides many options. It can be used for files,directories,mailslots,sockets, etc. In this case, we're only interested in standard files.

The C++ call looks like this:

HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile );

The python call is virtually the same with:

PyHANDLE = CreateFile( fileName, desiredAccess , shareMode , attributes , creationDisposition , flagsAndAttributes , hTemplateFile )

The module win32con in python is invaluable for setting most of these attributes. Besides win32con, you need win32security to create a security attribute.

Example

Here is a basic example of the raw program:

import win32file

import win32con

import win32security

import win32api

import pywintypes



highbits=0xffff0000 #high-order 32 bits of byte range to lock

file="c:\\\\wilma.txt"

secur_att = win32security.SECURITY_ATTRIBUTES()

secur_att.Initialize()



hfile=win32file.CreateFile( file,\\

			    win32con.GENERIC_READ|win32con.GENERIC_WRITE,\\

			    win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE,\\

			    secur_att,\\  #default

			    win32con.OPEN_ALWAYS,\\

			    win32con.FILE_ATTRIBUTE_NORMAL , 0 )



ov=pywintypes.OVERLAPPED() #used to indicate starting region to lock

win32file.LockFileEx(hfile,win32con.LOCKFILE_EXCLUSIVE_LOCK,0,highbits,ov)

win32api.Sleep(4000) #do something here

win32file.UnlockFileEx(hfile,0,highbits,ov)

hfile.Close()



<nl>Below, I have fleshed it out with a more useable Flock class.  The

code below works like this: You create an instance of the class,

providing a filename. It will create/access the file in a default way

and provide an hfile filehandle.  If you don't want the

default(shared/blocking), you can then specify in a dictionary what

type of locking you want.  Call the lock method on the file. Do

whatever you want with the hfile filehandle, then call the unlock

method which will remove the locks and close the filehandle.



Looking at the code below, for desiredAccess and shareMode, I have

both read and write on for most flexibility. The OPEN_ALWAYS means

that it will either use the current file or create a new one if none

is to be found. I use default security for the security attributes

option. The lock method basically determines what lock flags should be

used, depending on the type of locking you want and then calls

LockFileEx. An interesting option to LockFileEx is self.highbits.  You

can use that to specify portions of a file to lock instead of the

entire thing. When you're done with whatever you need to do, using the

hfile, filehandle, if necessary, then call the unlock method, to

remove the lock and close the filehandle.





<nl>Now for some code|





class Flock:

	def __init__(self,file):

		self.file=file

		self.type={'LOCK_EX':0,'LOCK_NB':0}

		secur_att = win32security.SECURITY_ATTRIBUTES()

		secur_att.Initialize()

		self.highbits=0xffff0000 #high-order 32 bits of byte range to lock

		#make a handel with read/write and open or create if doesn't exist

		self.hfile=win32file.CreateFile( self.file,\\

					win32con.GENERIC_READ|win32con.GENERIC_WRITE,\\

					win32con.FILE_SHARE_READ|win32con.FILE_SHARE_WRITE,\\

					secur_att,\\

					win32con.OPEN_ALWAYS,\\

					win32con.FILE_ATTRIBUTE_NORMAL , 0 )

	def lock(self):

		if self.type['LOCK_EX']:  #exclusive locking

			if self.type['LOCK_NB']: #don't wait, non-blocking

				lock_flags=win32con.LOCKFILE_EXCLUSIVE_LOCK|win32con.LOCKFILE_FAIL_IMMEDIATELY

			else: #wait for lock to free

				lock_flags=win32con.LOCKFILE_EXCLUSIVE_LOCK

		else: #shared locking

			if self.type['LOCK_NB']: #don't wait, non-blocking

				lock_flags=win32con.LOCKFILE_FAIL_IMMEDIATELY

			else:#shared lock wait for lock to free

				lock_flags=0

		self.ov=pywintypes.OVERLAPPED() #used to indicate starting region to lock

		win32file.LockFileEx(self.hfile,lock_flags,0,self.highbits,self.ov)

	def unlock(self):

		win32file.UnlockFileEx(self.hfile,0,self.highbits,self.ov) #remove locks

		self.hfile.Close()



l=Flock("c:\\\\a3.txt")

l.type['LOCK_EX']=0

l.type['LOCK_NB']=0



print 'calling lock'

l.lock()

print 'now locked '



win32api.Sleep(1000)

l.unlock()

print 'now unlocked'

Have a great time with programming with python!

John Nielsen   nielsenjf@my-deja.com