NAME

Dir::Flock - advisory locking of a dedicated directory

VERSION

0.08_01

SYNOPSIS

use Dir::Flock;
my $dir = Dir::Flock::getDir("/home/mob/projects/foo");
my $success = Dir::Flock::lock($dir);
# ... synchronized code
$success = Dir::Flock::unlock($dir);

# flock semantics
use Fcntl ':flock';
$success = Dir::Flock::flock($dir, LOCK_EX | LOCK_NB);
...
Dir::Flock::flock($dir, LOCK_UN);

# mutex/scoping semantics
{
    my $lock = Dir::Flock::lockobj($dir);
    ... synchronized code ...
}   # lock released when $lock goes out of scope

# code ref semantics
Dir::Flock::sync {
    ... synchronized code ...
}, $dir

DESCRIPTION

Dir::Flock implements advisory locking of a directory. The use case is to execute synchronized code (code that should only be executed by one process or thread at a time) or provide exclusive access to a file or other resource. Dir::Flock has more overhead than some of the other synchronization techniques available to Perl programmers, but it might be the only technique that works on NFS (Networked File System).

Algorithm

File locking is difficult on NFS because, as I understand it, each node maintains a cache that includes file contents and file metadata. When a system call wants to check whether a lock exists on a file, the filesystem driver might inspect the cached file rather than the file on the server, and it might miss an action taken by another node to lock a file.

The cache is not used, again, as I understand it, when the filesystem driver reads a directory. If advisory locking is accomplished through reading the contents of a directory, it will not be affected by NFS's caching behavior.

To acquire a lock in a directory, this module writes a small file into the directory. Then it checks if this new file is the "oldest" file in the directory. If it is the oldest file, then the process has acquired the lock. If there is already an older file in the directory, than that file specifies what process has a lock on the directory, and we have to wait and try again later. To unlock the directory, the module simply deletes the file in the directory that represents its lock.

Semantics

This module offers several different semantics for advisory locking of a directory.

functional semantics

The core Dir::Flock::lock and Dir::Flock::unlock functions begin and end advisory locking on a directory. All of the other semantics are implemented in terms of these functions.

$ok = Dir::Flock::lock( "/some/path" );
$ok = Dir::Flock::lock( "/some/path", $timeout );
$ok = Dir::Flock::unlock( "/some/path" );

flock semantics

The function Dir::Flock::flock emulates the Perl flock builtin, accepting the same arguments for the operation argument.

use Fcntl ':flock';
$ok = Dir::Flock::flock( "/some/path", LOCK_EX );
...
$ok = Dir::Flock::flock( "/some/path", LOCK_UN );

scope-oriented semantics

The Dir::Flock::lockobj function returns an object representing a directory lock. The lock is released when the object goes out of scope.

{
    my $lock = Dir::Flock::lockobj( "/some/path" );
    ...
}   # $lock out of scope, lock released

BLOCK semantics

The Dir::Flock::sync accepts a block of code or other code reference, to be executed with an advisory lock on a directory.

Dir::Flock::sync {
   ... synchronized code ...
} "/some/path";

FUNCTIONS

Most functions return a false value and set the package variable $Dir::Flock::errstr if they are unsuccessful.

lock

lock_ex

$success = Dir::Flock::lock( $directory [, $timeout ] )

$success = Dir::Flock::lock_ex( $directory [, $timeout ] )

Attempts to obtain an exclusive lock on the given directory. While the directory is locked, the lock or lock_sh call on the same directory from other processes or threads will block until the directory is unlocked (see "unlock"). Returns true if the lock was successfully acquired.

If an optional $timeout argument is provided, the function will try for at least $timeout seconds to acquire the lock, and return a false value if it is not successful in that time. Use a timeout of zero to make a "non-blocking" exclusive lock request.

lock_sh

$success = Dir::Flock::lock_sh( $directory [, $timeout ] )

Attempts to obtain a shared lock on the given directory. While there are shared locks on a directory, other calls to lock_sh may also receive a shared lock on the directory but calls to lock/lock_ex on the directory will block until all shared locks are removed.

If an optional $timeout argument is provided, the function will try for at least $timeout seconds to acquire the lock, and return a false value if it is not successful in that time. Use a timeout of zero to make a "non-blocking" shared lock request.

unlock

$success = Dir::Flock::unlock( $directory )

Releases the exclusive or shared lock on the given directory held by this process. Returns a false value if the current process did not possess the lock on the directory.

getDir

$tmp_directory = Dir::Flock::getDir( $root [,$persist] )

Creates a temporary and empty directory in a subdirectory of $root that is suitable for use as a synchronization directory. The directory will automatically be cleaned up when the process that called this function exits (unless a true value is provided for the optional $persist argument).

If the input to getDir is a filename rather than a directory name, a new subdirectory will be created in the directory where the file is located.

flock

$success = Dir::Flock::flock( $dir, $op )

Acquires and releases advisory locks on the given directory with the same semantics as the Perl builtin flock function.

lockobj

lockobj_ex

$lock = Dir::Flock::lockobj( $dir [, $timeout] );

$lock = Dir::Flock::lockobj_ex( $dir [, $timeout] );

Attempts to acquire an exclusive advisory lock for the given directory. On success, returns a handle to the directory lock with the feature that the lock will be released when the handle goes out of scope. This allows you to use this module with syntax such as

{
    my $lock = Dir::Flock::lockobj( "/some/path" );
    ... synchronized code ...
}
# $lock out of scope, so directory lock released
... unsynchronized code ...

Optional $timeout argument causes the function to block for a maximum of $timeout seconds attempting to acquire the lock. If $timeout is not provided or is undef, the function will block indefinitely while waiting for the lock.

Returns a false value and may sets $Dir::Flock::errstr if the function times out or is otherwise unable to acquire the directory lock.

lockobj_ex is an alias for lockobj.

lockobj_sh

my $lock = Dir::Flock::lockobj_sh($dir [, $timeout])

Analogue to "lockobj_ex". Returns a reference to a shared lock on a directory that will be released when the reference goes out of scope.

Returns a false value and may set $Dir::Flock::errstr if the function times out or otherwise fails to acquire a shared lock on the directory.

sync

sync_ex

$result = Dir::Flock::sync CODE $dir [, $timeout]

@result = Dir::Flock::sync_ex CODE $dir [, $timeout]

Semantics for executing a block of code while there is an advisory exclusive lock on the given directory. The code can be evaluated in both scalar or list contexts. An optional $timeout argument will cause the function to give up and return a false value if the lock cannot be acquired after $timeout seconds. Callers should be careful to distinguish cases where the specified code reference returns nothing and where the sync function times out and returns nothing. One way to distinguish these cases is to check the value of $Dir::Flock::errstr, which will generally be set if there was an issue with the locking mechanics.

The lock is released in the event that the given $code produces a fatal error.

sync_sh

$result = Dir::Flock::sync_sh BLOCK $dir [, $timeout]

@result = Dir::Flock::sync_sh BLOCK $dir [, $timeout]

Analogue of "sync_ex" but executes the code block while there is an advisory shared lock on the given directory.

DEPENDENCIES

Dir::Flock requires Time::HiRes where the Time::HiRes::time function has subsection resolution.

EXPORTS

Nothing is exported from Dir::Flock by default, but all of the functions documented here may be exported by name.

Many of the core functions of Dir::Flock have the same name as Perl builtin functions or functions from other popular modules, so users should be wary of importing functions from this module into their working namespace.

VARIABLES

PAUSE_LENGTH

$Dir::Flock::PAUSE_LENGTH

$Dir::Flock::PAUSE_LENGTH is the average number of seconds that the module will wait after a failed attempt to acquire a lock before attempting to acquire it again. The default value is 0.001, which is a good setting for having a high throughput when the synchronized operations take a short amount of time. In contexts where the synchronized operations take a longer time, it may be appropriate to increase this value to reduce busy-waiting CPU utilization.

LIMITATIONS

See "System requirements" above.

The Dir::Flock::Mock module can be loaded when necessary to provide a consistent synchronization API on systems that require Dir::Flock to work properly and on systems that don't support Dir::Flock.

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc Dir::Flock

You can also look for information at:

  • CPAN Ratings

    http://cpanratings.perl.org/d/Dir-Flock

  • <mob@cpan.org>

    With the decommissioning of http://rt.cpan.org/, please send bug reports and feature requests directly to the author's email address.

AUTHOR

Marty O'Brien, <mob@cpan.org>

LICENSE AND COPYRIGHT

Copyright (c) 2019-2020, Marty O'Brien

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.8 or, at your option, any later version of Perl 5 you may have available.

See http://dev.perl.org/licenses/ for more information.