#------------------------------------------------------------------------- # Lockfile class. Uses mkdir for a lock directory. (this had best # atomicity when we tested out various solutions) # # joelg@u.washington.edu #------------------------------------------------------------------------- package Lock; use strict; # Catch interrupt signals (like the user entering control-C on the command # line. other SIG's are available--see %SIG) $SIG{INT} = sub { print "\n\nExiting...\n\n"; exit 1; }; # object attribute name (hash key) that holds path to the lock dir my $LOCKDIR_KEY = '_ld'; # object attribute name (hash key) for whether we created the lock # directory. we'll need to know this in the destructor. another # process may have created the lock directory (and we were unsuccessful). # (Q: should we blow away this lockdir? A: only if we created it!) my $OUR_LOCKDIR_KEY = '_ol'; # how long will we wait for the lockfile my $SECONDS_TO_WAIT = 2; # when does a lockfile become stale? # -M returns number of days. This is 5 minutes my $LOCKFILE_STALE = 0.0035; # the calling script will look like this: # # my $lock = Lock->new('/tmp/names.txt'); # my $lock2 = Lock->new('/tmp/ages.txt'); # sub new { my $class = shift; my $filepath = shift; my $lockdir = $filepath . '.lock'; my $self = {}; bless $self, $class; $self->{$LOCKDIR_KEY} = $lockdir; # attempt the mkdir. my $count = 0; while (!mkdir($lockdir) and $count < $SECONDS_TO_WAIT) { # couldn't mkdir, but maybe the lockfile is stale? if (-M $lockdir > $LOCKFILE_STALE) { print "Blowing away stale lockdir.\n"; rmdir($lockdir) or die "Can't rmdir $lockdir"; next; } # nope. lockfile is current, so someone else has the file locked. # we wait. print "Waiting for lock...\n"; $count++; sleep 1; } # were we successful? if ($count >= $SECONDS_TO_WAIT) { die "Couldn't acquire lock!"; } # when this is set, we will rmdir the directory in the destructor $self->{$OUR_LOCKDIR_KEY} = 1; return $self; } sub get_lockfile_name { return $_[0]->{$LOCKDIR_KEY} } sub release_lock { my $self = shift; my $lockdir = $self->{$LOCKDIR_KEY}; # if we created the directory, we need to delete the directory. # otherwise, the directory wasn't created by this script, so we # shouldn't rmdir it. if ($self->{$OUR_LOCKDIR_KEY} and -d $lockdir) { rmdir ($lockdir) || die "Can't rmdir '$lockdir': $!"; } } sub DESTROY { my $self = shift; $self->release_lock; }