File locking
There are two general locking schemes, advisory locking and mandatory locking. Advisory locks have no effect on clients that do not explicitly check for a lock. Mandatory locks do not require clients to explicitly check for locks, but represent a security risk because it is possible to interfere with the normal operation of a system by placing mandatory locks on essential files. For this reason mandatory locks are excluded from the POSIX.1 standard. A platform file system usually supports either advisory or mandatory locking but not both.
The locking constants in the CfsConstants pool dictionary appear in the following table:
Table 12. File locking constants
Pool variable
Description
FRDLOCK
Specifies a shared (read) advisory lock. A shared advisory lock prevents any other client from setting an exclusive advisory lock on any portion of the protected area. Noncooperating clients can read or write in protected areas.
FWRLOCK
Specifies an exclusive (write) advisory lock. An exclusive advisory lock prevents any other client from setting a shared or exclusive advisory lock on any portion of the protected area. Noncooperating clients can read or write in protected areas.
FMDLOCK
Specifies an exclusive mandatory lock. An exclusive mandatory lock prevents any other client from reading, writing, or locking any portion of the protected area.
Determining supported lock types
In general, a platform operating system supports either advisory locking or mandatory locking, but not both. To determine whether a lock type is supported, send the supportsLockType: message to the CfsFileDescriptor class, with the desired locking constant as an argument. If the specified type of lock is supported, true is answered. Otherwise, false is answered. An example follows:
"Need exclusive lock, preferably mandatory. Answer what is available"
(CfsFileDescriptor supportsLockType: FMDLOCK)
ifTrue: [^FMDLOCK]
ifFalse: [
(CfsFileDescriptor supportsLockType: FWRLOCK)
ifTrue: [^FWRLOCK]
ifFalse: [^self error: 'No exclusive lock types are supported']].
Tip:
Locking might have no effect unless the file is being accessed by what the operating system considers to be distinct processes. With some network software, locking can only be effective if the file is being accessed from different machines. These are limitations in the services provided by the operating system or network.
Locking and unlocking regions
Sending the lock:start:len: message to a CfsFileDescriptor instance sets a segment lock on the file associated with the CfsFileDescriptor instance. The first argument is one of the file locking constants described in the table in the preceding section. The second argument is an integer specifying the zero-based offset of the first byte to be locked. The third argument is an integer specifying the number of bytes to be locked. If the specified locking constant is not supported, a CfsError instance is answered. To release a lock, use the unlock:start:len: message. The arguments are identical. Examples are as follows:
| fd theSize |
(fd := CfsFileDescriptor
open: 'lockable.fil'
oflag: ORDWR | OCREAT | OTRUNC) isCfsError
ifTrue: [^self error: fd message].
fd write: 'This is a TEST LOCK area' startingAt: 1 nbyte: 24.
"Lock the word test with a mandatory lock - should really determine appropriate
lock type first as shown in previous section on determining lock type"
fd lock: FMDLOCK start: 10 len: 4.
"Release the lock, must use same lock constant"
fd unlock: FMDLOCK start: 10 len: 4
"Lock the entire file and then unlock it"
"The size is saved so that the region is locked as was unlocked, even
if the file size increased between the operations."
fd lock: FMDLOCK start: 0 len: (theSize :=fd size).
fd unlock: FMDLOCK start: 0 len: theSize.
fd close.
When releasing a lock, the value of the lock type, start, and length arguments must be exactly the same as those used to set the lock. Otherwise the unlock operation can fail or have unpredictable behavior. Using the size message when releasing a lock as shown in the previous example is dangerous, because the size might have changed since the lock was set. Instead, save the parameters used when locking and reuse these values when releasing the lock. An example follows:
| fd len |
(fd := CfsFileDescriptor
open: 'lockable.fil'
oflag: ORDWR | OCREAT | OTRUNC) isCfsError
ifTrue: [^self error: fd message].
fd write: 'This is a TEST LOCK area' startingAt: 1 nbyte: 24.
fd lock: FMDLOCK start: 0 len: (len := fd size).
fd unlock: FMDLOCK start: 0 len: len.
fd close.
The effect of locking overlapping regions or releasing locks that have not been set is platform-specific. It is recommended that all locks be explicitly released before closing the file.
Last modified date: 05/12/2020