blipinsk has asked for the wisdom of the Perl Monks concerning the following question:
UPDATE: I just tried this on another box and it worked fine. The problem is on perl v5.8.9, HP-UX 11iv2, on itanium.
I'm trying to create a simple module for lock files, and I'm experiecing very strange behavior in trying to timeout the blocking flock() calls using $SIG{ALRM} and alarm(). Here is the module:
#!/usr/bin/perl package FileLock; use 5.00800; use strict; use warnings; use Fcntl qw(:flock); our $VERSION = 1.00; use constant NO_LOCK => 0; use constant SH_LOCK => 1; use constant EX_LOCK => 2; our $lock_stat = NO_LOCK; our $fh; sub new { # save off the class my $class = shift; # check for and save file if(scalar @_ < 1) { die("No file argument specified for FileLock->new().\n"); } my $file = shift; # open the file open($fh, '+<', $file) or die("Could not open '$file' in preparation + for lock.\n"); # create the object my $self = {'fh' => $fh, 'stat' => NO_LOCK}; bless($self, $class); return $self; } sub shared_lock { my $self = shift; # return if already an shared lock if($self->{'stat'} == SH_LOCK) { return 1; } # get the timeout if specified my $timeout = 0; if(scalar @_ > 0) { $timeout = shift; } # release existing lock before trying to take new if($self->{'stat'} != NO_LOCK) { $self->unlock or die("Could not unlock in preparation for shared l +ock.\n"); } # try and get a lock with the specified timeout eval { local $SIG{ALRM} = sub {die("timeout\n")}; alarm($timeout); flock(($self->{'fh'}), LOCK_SH) or die("flock failed during shared + lock.\n"); alarm(0); }; if ($@) { # if it was not a timeout then propogate it unless ($@ eq "timeout\n") { die; } # propogate the timeout die("Could not get shared lock within $timeout seconds.\n"); }; # got the lock, update status and done $self->{'stat'} = SH_LOCK; return 1; } sub exclusive_lock { my $self = shift; # return if already an exclusive lock if($self->{'stat'} == EX_LOCK) { return 1; } # get the timeout if specified my $timeout = 0; if(scalar @_ > 0) { $timeout = shift; } # release existing lock before trying to take new if($self->{'stat'} != NO_LOCK) { $self->unlock or die("Could not unlock in preparation for shared l +ock.\n"); } # try and get a lock with the specified timeout eval { local $SIG{ALRM} = sub {die("timeout\n")}; alarm($timeout); print " Doing flock\n"; flock(($self->{'fh'}), LOCK_EX) or die("flock failed during exclus +ive lock.\n"); print " Reset timeout\n"; alarm (0); }; if ($@) { # if it was not a timeout then propogate it unless ($@ eq "timeout\n") { die($@); } # propogate the timeout die("Could not get exclusive lock within $timeout seconds.\n"); }; # got the lock, update status and done $self->{'stat'} = EX_LOCK; return 1; } sub unlock { my $self = shift; # return if already unlocked if($self->{'stat'} == NO_LOCK) { return 1; } # try the unlock flock(($self->{'fh'}), LOCK_UN) or die("Could not release lock."); return 1; } 1; __END__
Here is test 1:
#!/usr/bin/perl -w # setup path to local modules BEGIN { use File::Basename; use Cwd; File::Basename::fileparse_set_fstype($^O); my $include = (File::Basename::fileparse($0))[1]; $include = $include eq '' ? '.' : $include; $include = Cwd::abs_path($include); unshift @INC, $include; } use strict; use FileLock; my $file = "filelock.sem"; my $lock = FileLock->new($file); print "Created FileLock obj for '$file'.\n"; # try a shared lock $lock->shared_lock(10); print "Got shared lock on '$file'.\n"; # try exclusive lock $lock->exclusive_lock(10); print "Got exclusive lock on '$file'.\n"; # try unlock $lock->unlock(); print "Unlocked '$file'.\n";
And test 2:
#!/usr/bin/perl -w # setup path to local modules BEGIN { use File::Basename; use Cwd; File::Basename::fileparse_set_fstype($^O); my $include = (File::Basename::fileparse($0))[1]; $include = $include eq '' ? '.' : $include; $include = Cwd::abs_path($include); unshift @INC, $include; } use strict; use FileLock; my $file = "filelock.sem"; my $lock = FileLock->new($file); print "Created FileLock obj for '$file'.\n"; # try a shared lock $lock->shared_lock(10); print "Got shared lock on '$file'.\n"; # hold the lock eval { while (1) { # do nothing } };
When I run test file 1, I get what I expect:
Created FileLock obj for 'filelock.sem'. Got shared lock on 'filelock.sem'. Doing flock Reset timeout Got exclusive lock on 'filelock.sem'. Unlocked 'filelock.sem'.
However, when I run test 2 in another shell and then run testfile 1 things stall. Test 2 works as anticipated and get a shared lock, and test 1 gets a shared lock, but then pauses indefinitely on the flock(). The alarm signal seems to be delayed or blocked by the flock. If I kill test 2 before 10 seconds then test 1 gets the exclusive lock and completes as expected. If I kill test 2 after ten seconds then test 1 completes with the timeout message. It seems that the alarm signal is being blocked/delayed by flock(), any ideas why and how to fix it? I've listed examples of killing test 2 before and after 10 seconds of test 1 starting below:
Before 10 seconds: Created FileLock obj for 'filelock.sem'. Got shared lock on 'filelock.sem'. Doing flock Reset timeout Got exclusive lock on 'filelock.sem'. Unlocked 'filelock.sem'. After ten seconds: Created FileLock obj for 'filelock.sem'. Got shared lock on 'filelock.sem'. Doing flock Could not get exclusive lock within 10 seconds.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: strange flock and alarm behavior (safe signals)
by tye (Sage) on Jan 17, 2011 at 20:14 UTC | |
|
Re: strange flock and alarm behavior
by blipinsk (Initiate) on Jan 17, 2011 at 21:27 UTC | |
by afoken (Chancellor) on Jan 17, 2011 at 22:38 UTC | |
|
Re: strange flock and alarm behavior
by tilly (Archbishop) on Jan 17, 2011 at 22:58 UTC | |
|
Re: strange flock and alarm behavior
by locked_user sundialsvc4 (Abbot) on Jan 17, 2011 at 19:05 UTC |