Skip to content
ggodart edited this page Jan 6, 2021 · 1 revision

vsLock

See original

SYNOPSIS

 use vsLock qw(lock trylock unlock);

 # Simple locking using default settings
 lock("/some/file") || die "can't lock /some/file\n";
 warn "already locked\n" unless trylock("/some/file");
 unlock("/some/file");

 # Build customized locking manager object
 $lockmgr = new vsLock(-format => '%f.lck',
        -max => 20, -delay => 1, -nfs => 1);

 $lockmgr->lock("/some/file") || die "can't lock /some/file\n";
 $lockmgr->trylock("/some/file");
 $lockmgr->unlock("/some/file");

 $lockmgr->configure(-nfs => 0);

DESCRIPTION

This simple locking scheme is not based on any file locking system calls such as flock() or lockf() but rather relies on basic file system primitives and properties, such as the atomicity of the write() system call. It is not meant to be exempt from all race conditions, especially over NFS. The algorithm used is described below in the ALGORITHM section.

It is possible to customize the locking operations to attempt locking once every 5 seconds for 30 times, or delete stale locks (files that are deemed too ancient) before attempting the locking.

ALGORITHM

The locking alogrithm attempts to create a lockfile using a temporarily redefined umask (leaving only read rights to prevent further create operations). It then writes the process ID (PID) of the process and closes the file. That file is then re-opened and read. If we are able to read the same PID we wrote, and only that, we assume the locking is successful.

When locking over NFS, i.e. when the one of the potentially locking processes could access the lockfile via NFS, then writing the PID is not enough. We also write the hostname where locking is attempted to ensure the data are unique.

CUSTOMIZING

Customization is only possible by using the object-oriented interface, since the configuration parameters are stored within the object. The object creation routine make can be given configuration parmeters in the form a "hash table list", i.e. a list of key/value pairs. Those parameters can later be changed via configure by specifying a similar list of key/value pairs.

To benefit from the bareword quoting Perl offers, all the parameters must be prefixed with the - (minus) sign, as in -format for the format parameter.. However, when querying the object, the minus must be omitted, as in $obj->format.

Here are the available configuration parameters along with their meaning, listed in alphabetical order:

Parameter Description
delay The amount of seconds to wait between locking attempts when the file appears to be already locked. Default is 2 seconds.
ext The locking extension that must be added to the file path to be locked to compute the lockfile path. Default is .lock (note that . is part of the extension and can therefore be changed). Ignored when format is also used.
format Using this parmeter supersedes the ext parmeter. The formatting string specified is run through a rudimentary macro expansion to derive the lockfile path from the file to be locked. The following macros are available:
%% A real % sign
%f The full file path name
%D The directory where the file resides
%F The base name of the file
%p The process ID (PID)
The default is to use the locking extension, which itself is .lock, so it is as if the format used was %f.lock, but one could imagine things like /var/run/%F.%p, i.e. the lockfile does not necessarily lie besides the locked file (which could even be missing).
When locking, the locking format can be specified to supersede the object configuration itself. Be sure to use the same locking format when unlocking! For instance, you can say:
$obj->lock('ppp', '/var/run/ppp.%p');
$obj->configure(-format => '/var/run/ppp.%p');
$obj->unlock('ppp'); # Okay, since format changed
This also works when the calling lock() without an object, and this is where it is most useful since in that case you have no object to configure! The example above becomes:
lock('ppp', '/var/run/ppp.%p'); # file ppp may not even exist!
<do whatever>
unlock('ppp', '/var/run/ppp.%p'); # MUST specify here
hold Maximum amount of seconds we may hold a lock. Past that amount of time, an existing lockfile is removed, being taken for a stale lock. Default is 3600 seconds. Specifying 0 prevents any forced unlocking.
max Amount of times we retry locking when the file is busy, sleeping delay seconds between attempts. Defaults to 30.
nfs A boolean flag, false by default. Setting it to true means we could lock over NFS and therefore the hostname must be included along with the process ID in the stamp written to the lockfile.
wafter Stands for warn after. It is the number of seconds past the first warning during locking time after which a new warning should be emitted. See warn and wmin below. Default is 20.
warn A boolean flag, true by default. To suppress any warning, set it to false.
wfunc A function pointer to dereference when a warning is to be issued. By default, it points to Perl's warn() function.
wmin The minimal amount of time when waiting for a lock after which a first warning must be emitted, if warn is true. After that, a warning will be emitted every wafter seconds. Defaults to 15.

Each of those configuration attributes can be queried on the object directly:

        $obj = vsLock->make(-nfs => 1);
        $on_nfs = $obj->nfs;

Those are pure query routines, i.e. you cannot say:

        $obj->nfs(0);                   # WRONG
        $obj->configure(-nfs => 0);     # Right

to turn of the NFS attribute. That is because my OO background chokes at having querying functions with side effects.

INTERFACE

The OO interface documented below specifies the signature and the semantics of the operations. Only the lock, trylock and unlock operation can be imported and used via a non-OO interface, with the exact same signature nonetheless.

The interface contains all the attribute querying routines, one for each configuration parmeter documented in the CUSTOMIZING section above, plus, in alphabetical order:

configure(-key => value, -key2 => value2, ...)

Change the specified configuration parameters and silently ignore the invalid ones.

lock(file, format)

Attempt to lock the file, using the optional locking format if specified, otherwise using the default format scheme configured in the object, or by simply appending the ext extension to the file.

If the file is already locked, sleep delay seconds before retrying, repeating try/sleep at most max times. If warning is configured, a first warning is emitted after waiting for wmin seconds, and then once every wafter seconds, via the wfunc routine.

Before the first attempt, and if hold is non-zero, any existing lockfile is checked for being too old, and it is removed if found to be stale. A warning is emitted via the wfunc routine in that case, if allowed.

Returns true if the file has been successfully locked.

lockfile(file, format)

Simply compute the path of the lockfile that would be used by the lock procedure if it were passed the same parameters.

make(-key => value, -key2 => value2, ...)

The creation routine for the simple lock object. Returns a blessed hash reference.

trylock(file, format)

Same as lock except that it immediately returns false and does not sleep if the to-be-locked file is busy, i.e. already locked. Any stale locking file is removed, as lock would do anyway.

Returns true if the file has been successfully locked.

unlock(file, format)

Unlock the file. If the optional format parameter is given, it must be the same as the one that was used at lock time.

BUGS

The algorithm is not bullet proof. It's only reasonably safe. Don't bet the integrity of a mission-critical database on it though.

The sysopen() call should probably be used with the O_EXCL|O_CREAT flags to be on the safer side. Still, over NFS, this is not an atomic operation anyway.

AUTHOR

Raphael Manfredi Raphael_Manfredi@grenoble.hp.com

SEE ALSO

File::Flock(3).

Clone this wiki locally