The lock file has in principle world read/write access, so every process accessing the main file can write information in it. The lock file contains the following information (in canonical format):
Acquiring a lock works as follows:
When the lock file cannot be opened as read/write, it is opened as
readonly. It means that the request list cannot be stored in it,
so the process has no way to tell the other processes it wants
access to the file. It has to wait until the lock is released.
In principle a lock file should always be there. However, it
is possible (with a constructor option) that there is no lock file.
In that case each lock request succeeds without doing actual locking.
This mode is needed to be able to handle readonly tables containing
no lock file.
After each write the fsync function is called to make sure that the contents of the file are written to disk. This is necessary for correct file synchronization in NFS. However, at the moment this feature is switched off, because it degraded performance severely.
Apart from the read/write lock handling, the LockFile
also contains a mechanism to detect if a file is opened by another
process. This can be used to test if a process can safely delete the file.
For this purpose it sets another read lock when the file gets opened.
The function isMultiUsed tests this lock to see if the file is
used in other processes.
This lock is also used to tell if the file is permanently locked.
If that is the case, the locked block is 2 bytes instead of 1.
When in the same process multiple LockFile objects are created for the same file, deleting one object releases all locks on the file, thus also the locks held by the other LockFile objects. This behaviour is due to the way file locking is working on UNIX machines (certainly on Solaris 2.6). One can use the test program tLockFile to test for this behaviour.
// Create/open the lock file (with 1 sec inspection interval). // Acquire the lock and get the synchronization info. LockFile lock ("file.name", 1); MemoryIO syncInfo; if (! lock.acquire (syncInfo)) { throw (AipsError ("Locking failed: " + lock.message())); } while (...) { ... do something with the table files ... // Test if another process needs the files. // If so, synchronize files and release lock. if (lock.inspect()) { do fsync for all other files syncInfo.seek (0); syncInfo.write (...); lock.release (syncInfo); // At this point another process can grab the lock. // Reacquire the lock lock.acquire (syncInfo); throw (AipsError ("Locking failed: " + lock.message())); } } }
The destructor does not delete the file, because it is not known when the last process using the lock file will stop. For the table system this is no problem, because the lock file is contained in the directory of the table, thus deleted when the table gets deleted.
Is the file associated with the LockFile object in use in another process?
Acquire a read or write lock.
It reads the information (if the info argument is given)
from the lock file. The user is responsible for interpreting the
information (e.g. converting from canonical to local format).
The seek pointer in the MemoryIO object is set to 0,
so the user can simply start reading the pointer.
The argument nattempts tells how often it is
attempted (with 1 second intervals) to acquire the lock if
it does not succeed.
0 means forever, while 1 means do not retry.
Release a lock and write the information (if given) into the lock file. The user is responsible for making the information machine-independent (e.g. converting from local to canonical format).
Inspect if another process wants to access the file (i.e. if the request list is not empty). It only inspects if the time passed since the last inspection exceeds the inspection interval as given in the constructor. If the time passed is too short, False is returned (indicating that no access is needed). If always==True, no test on inspection interval is done, so the inspect is always done.
Test if the file can be locked for read or write.
Test if the process has a lock for read or write on the file.
Get the last error.
Get the message belonging to the last error.
Get the name of the lock file.
Get the block of request id's.
Get the request id's and the info from the lock file.
Put the info into the file (after the request id's).
Tell if another process holds a read or write lock on the given file
or has the file opened. It returns:
3 if write-locked elsewhere.
2 if read-locked elsewhere.
1 if opened elsewhere.
0 if locked nor opened.
It fills in the PID of the process having the file locked or opened.
If locked, it also tells if it is permanently locked.
An exception is thrown if the file does not exist or cannot
be opened.
Assignment cannot be used (its semantics are too difficult).
Get an Int from the buffer at the given offset and convert it from canonical to local format. If the buffer is too short (i.e. does not contain the value), a zero value is returned.
Add the request id of this process to the list.
Remove the request id of this process from the list (and all the ones before it).
Get the request list from the file.
Put the request list into the file.
Convert the request id from canonical to local format.
Get the number of request id's.