On Thu, 10 Jan 2002 10:00:50 +0000 (GMT), Richard Smith wrote:
>I use an indirect method for locking files, which works fine on all platforms.
>I borrowed the method from a Matthew Wright script
Uh-oh...
>(tweaked a bit for my own
>taste in Perl style). The subroutines are as follows:
>
>sub lockFile {
> # Create a lock file for the file passed as a parameter.
> # If no parameter is passed, return a false value.
> $_ = shift or return 0;
No local()?
> $_ = $_ . '.lock';
> my $i;
>
> for ($i = 0; $i < $timeOut; $i++) {
>
> (-e $_)
> and sleep 1
> and next;
>
> open LOCK, ">$_" or &outputError("Can't create lock file",
>$!);
That's what I thought. You've got a race condition. ANother program
might create the lockfile between your "the file does not exist" test,
and your "I'll make it now".
> print LOCK '0';
> close LOCK;
> last;
>
> }
There are better ways, to avoid the race condition. You can use
sysopen(), which allows for more ways to create a file than the plain
">" and ">>" modes: such as the O_EXCL (flag) mode You need some
constants from Fcntl.
And sleeping in increments of 1 second is far too course. Either use a
shorter sleep from Time::HiRes, or you could use the 4 argument select()
to produce the time-out.
You also need to delete the lockfile when you're through. That's
dangerous: if your script dies, the lockfile won't be deleted. Unless
you take care of that...
Here's a draft, I haven't thoroughly tested it.
use Fcntl;
{
my @lockfiles;
END {
foreach(@lockfiles) { unlink } # for 5.004
}
sub lockFile ($) {
#Create a lock file for the file passed as a parameter.
my $lockfile = shift() . '.lock';
my $t0 = time;
local *LOCK;
until(sysopen LOCK, $lockfile,
O_CREAT|O_TRUNC|O_EXCL|O_WRONLY) {
die "Too many loops" if time-$t0 > 10;
select undef, undef, undef, 0.1;
}
push @lockfiles, $lockfile;
print LOCK $$;
close LOCK;
}
}
--
Bart.