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

    Are you aware of Perl "safe signals"? It might be that some versions of Perl don't unblock signals during calls to whatever is used to implement flock in that version.

    - tye        

Re: strange flock and alarm behavior
by blipinsk (Initiate) on Jan 17, 2011 at 21:27 UTC
    I have seen that signal info before. I've done some more digging, and I can of course avoid the problem by just looping and using the LOCK_NB flag. I've implemented and tested that with success. The file I was locking was also on NFS, and when I tried it with a local file the problem does not exist either. This is good enough for my purposes, since I'm be using it on a local file in actual usage. My development is just done on an NFS share.

      At least some NFS implementations and locks don't mix too well. Several experts consider locks on NFS at least unreliable. Maybe things changed with newer NFS versions, but I would not bet on that.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: strange flock and alarm behavior
by tilly (Archbishop) on Jan 17, 2011 at 22:58 UTC
    Why are you trying to create a simple module for lockfiles? Did you encounter problems with existing modules like LockFile::Simple?
Re: strange flock and alarm behavior
by locked_user sundialsvc4 (Abbot) on Jan 17, 2011 at 19:05 UTC

    I have never found an flock-based implementation that worked satisfactorily well in a Unix environment “in the general case.”   None.   Never.   I have long ago given up on the very thought of it...

    I am eagerly awaiting the chance to be proven wrong, because the status quo is a Royal PITA.