The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Dir::Flock - advisory locking of a dedicated directory

VERSION

0.09

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 on a directory, similar to how the builtin flock performs advisory locking on a file. In addition to helping facilitate synchronized access to a directory, Dir::Flock functions can be adapted to provide synchronized access to any resource across multiple processes and even across multiple hosts (where the same directory has been mounted). It overcomes some of the limitations of flock such as heritability of locks over forks and threads, or being able to lock files on a networked file system (like NFS).

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 an empty file into the directory. The name of the file encodes the host and process id that is requesting the lock, and a timestamp of when the request was made. 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, then the process specified by that file possesses the lock, and we must 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. Note that the first argument is a path name, not a directory handle.

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" request for an exclusive lock.

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 shared 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 the optional $persist argument is set to a true value. The $persist argument can be used to create a directory that can be used for synchronization by other processes or on other hosts after the lifetime of the current program.

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 fails to acquire the lock 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 BLOCK produces a fatal error.

sync_sh

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

@result = Dir::Flock::sync_sh CODE $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 subsecond 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

Dir::Flock requires that the function values of Time::HiRes have subsecond resolution

If Dir::Flock is going to be used to synchronize a networked directory across multiple hosts, it is imporant that the clocks be synchronized between those hosts. Otherwise, a host with a "fast" clock will be able to steal locks from a host with a "slow" clock.

SEE ALSO

Many other modules have taken a shot at advisory locking over NFS, including Mail::Box::Locker::NFS, File::NFSLock. File::SharedNFSLock, and IPC::ConcurrencyLimit::Lock::NFS.

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-2021, 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.