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

NAME

Lock::Socket - application lock/mutex module based on sockets

VERSION

0.0.6 (2014-09-15)

SYNOPSIS

    ### Function API ###
    use Lock::Socket qw/lock_socket try_lock_socket/;

    # Raises exception if cannot lock
    my $lock = lock_socket(5197);

    # Or just return undef
    my $lock2 = try_lock_socket(5197)
      or die "handle your own error";

    ### Object API ###
    use Lock::Socket;

    # Create a socket
    my $sock = Lock::Socket->new( port => 5197 );

    # Lock or raise an exception
    $sock->lock;

    # Can check its status in case you forgot
    my $status = $sock->is_locked;    # 1 (or 0)
    my $addr   = $sock->addr;
    my $port   = $sock->port;

    # Re-locking changes nothing
    $sock->lock;

    # New lock on same port fails
    my $sock2 = Lock::Socket->new( port => 5197 );
    eval { $sock2->lock };            # exception

    # But trying to get a lock is ok
    my $status      = $sock2->try_lock;     # 0
    my $same_status = $sock2->is_locked;    # 0

    # If you need the underlying filehandle
    my $fh = $sock->fh;

    # You can manually unlock
    $sock->unlock;

    # ... or unlocking is automatic on scope exit
    undef $sock;

DESCRIPTION

Lock::Socket provides cooperative inter-process locking for applications that need to ensure that only one process is running at a time. This module works by binding an INET socket to a port on a loopback address which the operating system conveniently restricts to a single process.

Should you use Lock::Socket instead of a file-based module? Perhaps. Here are some statements that I believe to be true that work in its favour:

  • Lock::Socket guarantees (through the operating system) that no two applications will hold the same lock: there is no race condition.

  • Lock::Socket is guaranteed (again through the operating system) to clean up neatly when your process exits, so there are no stale locks to deal with.

  • Lock::Socket relies on functionality that is well supported by anything that Perl runs on: no issues with flock(2) support on Win32 for example.

The following statements I also believe to be true that work against the module:

  • There is a slight chance that some unrelated process can grab the lock that you need by accident, as the available lock namespace is system-wide (we can't use user directories).

  • Lock::Socket has no ability to identify which process is holding a lock.

  • Lock::Socket cannot be used for locking access to files on NFS shares, only local resources.

I'll leave it up to you and your particular situation to know if this is the right module for you.

Function Interface

lock_socket($PORT, [$ADDR]) -> Lock::Socket

Attempts to lock $PORT (on 127.0.0.1 by default) and returns a Lock::Socket object. Raises an exception if the lock cannot be taken.

try_lock_socket($PORT, [$ADDR]) -> Lock::Socket | undef

Same as lock_socket() but returns undef on failure.

lock_user_socket($PORT, [$ADDR]) -> Lock::Socket

Similarly to lock_socket() this function attempts to take a lock and returns a Lock::Socket object or raises an exception if the lock cannot be taken. The difference here is that this function attempts to take a lock that is per-user, instead of system wide.

  • The actual lock port is calculated as $PORT + $UID.

  • The loopback $ADDR by default is calculated as follows:

        Octet   Value
        ------  ------------------------------
        1       127
        2       First byte of user ID
        3       Second byte of user ID
        4       1

Unfortunately on BSD systems the loopback interface appears to be configured with a /32 netmask so there the above calculation is not performed and the address defaults to 127.0.0.1.

try_lock_user_socket($PORT, [$ADDR]) -> Lock::Socket | undef

Same as lock_user_socket() but returns undef on failure.

Object Interface

Objects are instantiated manually as follows.

    my $sock = Lock::Socket->new(
        port => $PORT, # required
        addr => $ADDR, # defaults to 127.0.0.1
    );

As soon as the Lock::Socket object goes out of scope (or rather the underlying filehandle object) the port is closed and the lock can be obtained by someone else.

Holding a lock over 'exec'

If you want to keep holding onto a lock socket after a call to exec (perhaps after forking) you should read about the $^F variable in perlvar, as you have to set it before creating a lock socket to ensure the socket will not be closed on exec.

Example application

See the example/solo file in the distribution for a Lock::Socket demonstration which provides a command-line lock:

    usage: solo PORT COMMAND...

    # terminal 1
    $ example/solo 1414 sleep 10  # Have lock on 127.3.232.1:1414

    # terminal 2
    $ example/solo 1414 sleep 10  # bind error

CAVEATS

Most operating systems implement the Ephemeral Port concept. If you select a port from that range it could be possible that some unrelated process uses, if temporarily, the port that your application defines for locking.

Unfortunately the ephemeral port range varies from system to system. Based on the wikipedia page mentioned above, chances are good that a port between 5001 and 32767 will work, particularly if your system loopback device is configured with a /8 netmask (i.e. supports the 127.X.Y.1 scheme). To be sure you should investigate the platorms your application runs on, and possibly choose an appropriate value at runtime.

SEE ALSO

There are many other locking modules available on CPAN, most of them relying on some type of file lock.

AUTHOR

Mark Lawrence <nomad@null.net>. This module was inspired by the solo.pl script by Andres Erbsen.

COPYRIGHT AND LICENSE

Copyright (C) 2014 Mark Lawrence <nomad@null.net>

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.