The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Dir::Flock - advisory locking of a dedicated directory

VERSION

0.02

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

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 )

Creates a temporary and empty directory in a subdirectory of $root that is suitable for use as a synchronization object. The directory will automatically be cleaned up when the process that called this function exits.

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 set $! 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 $! 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.

The lock is released in the even 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.

LIMITATIONS

Requires a version of Time::HiRes with the stat function, namely v1.92 or better (though later versions seem to have some fixes related to the stat function). Requires operating system support for subsecond file timestamp (output &Time::HiRes::d_hires_stat and look for a positive value to indicate that your system has such support) and filesystem support (FAT is not likely to work).

SUPPORT

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

    perldoc Forks::Queue

You can also look for information at:

AUTHOR

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

LICENSE AND COPYRIGHT

Copyright (c) 2019, 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.