Saturday, November 7, 2009

File Locking









File Locking


An important issue in any system with multiple processes is coordination and synchronization of access to shared objects, such as files.


Windows can lock files, in whole or in part, so that no other process (running program) can access the locked file region. File locks can be read-only (shared) or read-write (exclusive). Most important, the locks belong to the process. Any attempt to access part of a file (using ReadFile or WriteFile) in violation of an existing lock will fail because the locks are mandatory at the process level. Any attempt to obtain a conflicting lock will also fail even if the process already owns the lock. File locking is a limited form of synchronization between concurrent processes and threads; synchronization is covered in much more general terms starting in Chapter 8.


The most general function is LockFileEx. The less general function, LockFile, can be used on Windows 9x.


LockFileEx is a member of the extended I/O class of functions, so the overlapped structure, used earlier to specify file position to ReadFile and WriteFile, is required to specify the 64-bit file position and range of the file region that is to be locked.



BOOL LockFileEx (
HANDLE hFile,
DWORD dwFlags,
DWORD dwReserved,
DWORD nNumberOfBytesToLockLow,
DWORD nNumberOfBytesToLockHigh,
LPOVERLAPPED lpOverlapped)


LockFileEx locks a byte range in an open file for either shared (multiple readers) or exclusive (one reader-writer) access.


Parameters


hFile is the handle of an open file. The handle must have GENERIC_READ or both GENERIC_READ and GENERIC_WRITE file access.


dwFlags determines the lock mode and whether to wait for the lock to become available.


LOCKFILE_EXCLUSIVE_LOCK, if set, indicates a request for an exclusive, read-write lock. Otherwise, it requests a shared (read-only) lock.


LOCKFILE_FAIL_IMMEDIATELY, if set, specifies that the function should return immediately with FALSE if the lock cannot be acquired. Otherwise, the call blocks until the lock becomes available.


dwReserved must be 0. The two parameters with the length of the byte range are self-explanatory.


lpOverlapped points to an OVERLAPPED data structure containing the start of the byte range. The overlapped structure contains three data members that must be set (the others are ignored); the first two determine the start location for the locked region.


  • DWORD Offset (this is the correct name; not OffsetLow).

  • DWORD OffsetHigh.

  • HANDLE hEvent should be set to 0.


A file lock is removed using a corresponding UnlockFileEx call; all the same parameters are used except dwFlags.



BOOL UnlockFileEx (
HANDLE hFile,
DWORD dwReserved,
DWORD nNumberOfBytesToLockLow,
DWORD nNumberOfBytesToLockHigh,
LPOVERLAPPED lpOverlapped)


You should consider several factors when using file locks.


  • The unlock must use exactly the same range as a preceding lock. It is not possible, for example, to combine two previous lock ranges or unlock a portion of a locked range. An attempt to unlock a region that does not correspond exactly with an existing lock will fail; the function returns FALSE and the system error message indicates that the lock does not exist.

  • Locks cannot overlap existing locked regions in a file if a conflict would result.

  • It is possible to lock beyond the range of a file's length. This approach could be useful when a process or thread extends a file.

  • Locks are not inherited by a newly created process.


Table 3-1 shows the lock logic when all or part of a range already has a lock. This logic applies even if the lock is owned by the same process that is making the new request.


Table 3-1. Lock Request Logic
 

Requested Lock Type

Existing Lock

Shared Lock

Exclusive Lock

None

Granted

Granted

Shared lock (one or more)

Granted

Refused

Exclusive lock

Refused

Refused



Table 3-2 shows the logic when a process attempts a read or write operation on a file region with one or more locks, owned by a separate process, on all or part of the read-write region. A failed read or write may take the form of a partially completed operation if only a portion of the read or write record is locked.


Table 3-2. Locks and I/O Operation
 

I/O Operation

Existing Lock

Read

Write

None

Succeeds

Succeeds

Shared lock (one or more)

Succeeds. It is not necessary for the calling process to own a lock on the file region.

Fails

Exclusive lock

Succeeds if the calling process owns the lock. Fails otherwise.

Succeeds if the calling process owns the lock. Fails otherwise.



Read and write operations are normally in the form of ReadFile and WriteFile calls or their extended versions, ReadFileEx and WriteFileEx. Diagnosing a read or write failure requires calling GetLastError.


Accessing memory that is mapped to a file is another form of file I/O, as will be discussed in Chapter 5. Lock conflicts are not detected at the time of memory reference; rather, they are detected at the time that the MapViewOfFile function is called. This function makes a part of the file available to the process, so the lock must be checked at that time.


The LockFile function is a limited, special case and is a form of advisory locking. It can be used on Windows 9x, which does not support LockFileEx. Only exclusive access is available, and LockFile returns immediately. That is, LockFile does not block. Test the return value to determine whether you obtained the lock.


Releasing File Locks


Every successful LockFileEx call must be followed by a single matching call to UnlockFileEx (the same is true for LockFile and UnlockFile). If a program fails to release a lock or holds the lock longer than necessary, other programs may not be able to proceed, or, at the very least, their performance will be negatively impacted. Therefore, programs should be carefully designed and implemented so that locks are released as soon as possible, and logic that might cause the program to skip the unlock should be avoided.


Termination handlers (Chapter 4) are a useful way to ensure that the unlock is performed.


Lock Logic Consequences


Although the file lock logic shown in Tables 3-1 and 3-2 is natural, it has consequences that may be unexpected and cause unintended program defects. Here are some examples.


  • Suppose that process A and process B periodically obtain shared locks on a file, and process C blocks when attempting to gain an exclusive lock on the same file after process A gets its shared lock. Process B may now gain its shared lock even though C is still blocked, and C will remain blocked even after A releases the lock. C will remain blocked until all processes release their shared locks even if they obtained them after C blocked. In this scenario, it is possible that C will be blocked forever even though all the other processes manage their shared locks properly.

  • Assume that process A has a shared lock on the file and that process B attempts to read the file without obtaining a shared lock first. The read will still succeed even though the reading process does not own any lock on the file because the read operation does not conflict with the existing shared lock.

  • These statements apply both to entire files and to regions.

  • A read or write may be able to complete a portion of its request before encountering a conflicting lock. The read or write will return FALSE, and the byte transfer count will be less than the number requested.


Using File Locks

File locking examples are deferred until Chapter 6, which covers process management. Program 4-2, 6-4, 6-5, and 6-6 use locks to ensure that only one process at a time can modify a file.



UNIX has advisory file locking; an attempt to obtain a lock may fail (the logic is the same as in Table 3-1), but the process can still perform the I/O. Therefore, UNIX can achieve locking between cooperating processes, but any other process can violate the protocol.


To obtain an advisory lock, use options to the fcntl function. The commands (the second parameter) are F_SETLK, F_SETLKW (to wait), and F_GETLK. An additional block data structure contains a lock type that is one of F_RDLCK, F_WRLCK, or F_UNLCK and the range.


Mandatory locking is also available in some UNIX systems using a file's set-group-ID and group-execute, both using chmod.


UNIX file locking behavior differs in many ways. For example, locks are inherited through an exec call.


The C library does not support locking, although Visual C++ does supply nonstandard extensions for locking.










    No comments:

    Post a Comment