But what will you do while someone else has the lock? Quit and try later? Busy-wait?
If you don't need synchronization, then a good bet is sysopen
with the O_EXCL
and O_CREAT
flags set, which will create the file only if it doesn't exist.
use Fcntl qw/ :DEFAULT /;
# ...
sysopen my $fh, $LOCKFILE, O_EXCL|O_CREAT
or die "$0: sysopen: $!";
But note the following caveat from the Linux open(2)
manual page:
O_EXCL
is only supported on NFS when using NFSv3 or later on kernel 2.6 or later. In environments where NFS O_EXCL
support is not provided, programs that rely on it for performing locking tasks will contain a race condition. Portable programs that want to perform atomic file locking using a lockfile, and need to avoid reliance on NFS support for O_EXCL
, can create a unique file on the same file system (e.g., incorporating hostname and PID), and use link(2)
to make a link to the lockfile. If link(2)
returns 0, the lock is successful. Otherwise, use stat(2)
on the unique file to check if its link count has increased to 2, in which case the lock is also successful.
“I’d rather have a network filesystem than NFS,” as the saying goes, so keep your coordinating processes on the same machine if you can.
You might consider using flock
as in the code below:
#! /usr/bin/perl
use warnings;
use strict;
use Fcntl qw/ :DEFAULT :flock /;
my $LOCKFILE = "/tmp/mylock";
sub acquire_lock {
sysopen my $fh, $LOCKFILE, O_RDWR|O_CREAT or die "$0: open: $!";
flock $fh, LOCK_EX or die "$0: flock: $!";
$fh;
}
sub work {
for (1 .. 2) {
my $fh = acquire_lock;
print "$0: $$ has lock\n";
sleep rand 3;
close $fh or warn "$0: [$$] close: $!";
}
exit;
}
For a demo, the code below forks five children that take turns acquiring the lock:
my $KIDS = 5;
my %pids;
for (1 .. $KIDS) {
my $pid = fork;
die "$0: fork: $!" unless defined $pid;
$pid ? ++$pids{$pid} : work;
}
while (my $pid = wait) {
last if $pid == -1;
warn "$0: unknown child $pid" unless delete $pids{$pid};
}
warn "$0: still alive: " .
join(", " => sort { $a <=> $b } keys %pids) .
"\n"
if keys %pids;
Sample output:
./kidlock: 26644 has lock
./kidlock: 26645 has lock
./kidlock: 26646 has lock
./kidlock: 26645 has lock
./kidlock: 26648 has lock
./kidlock: 26646 has lock
./kidlock: 26647 has lock
./kidlock: 26647 has lock
./kidlock: 26644 has lock
./kidlock: 26648 has lock