December 3, 2008.
Recently at work I needed to write a script that would walk through a directory's contents that matched a regex, perform some operations on the files, and then remove the files afterwards. I didn't want to have the script running all the time, so I decided to add a cronjob to run the script every minute (if there aren't any files to process, then the script terminates immediately).
But there was a wrinkle to my plan: the script could potentially run for longer than one minute, and having two copies of the script running simultaniously would be a Bad Thing. After considering this problem for a few minutes I realized a simple and sufficient solution: using a file as a mutex.
The system's functioning is pretty basic:
my_script.lock
exists.
my_script.lock
.
At first I didn't bother putting a timestamp into the file, but by adding a timestamp it makes it possible to expire the lock if it has been locked for an unreasonably long period of time (suggesting that the script crashed) without manually deleting the lock file.
Implementing this file-based mutex in Perl is quick and painless. Unless you're a miserable Perl coder, then it'll take you a while to piece together all the necessary documentation.
It took me a while to piece it together.
my $lock_file = '/tmp/my_script.lock';
my $expire_lock_after = 60 * 30;
sub is_locked {
# -e is a function that checks for file existence
if (-e $lock_file) {
# this line declares there is no line delimiter
local $/=undef;
open LOCK, "<$lock_file" or warn "Couldn't open $lock_file";
my $ts = <LOCK>;
close LOCK;
if ( $ts > time() - $expire_lock_after ) {
return 1;
}
}
return undef;
}
sub lock {
open LOCK, ">$lock_file" or warn "Couldn't open $lock_file";
print LOCK time();
close LOCK;
}
sub unlock {
unlink($lock_file);
}
if (is_locked) die "$lock_file is locked";
lock();
print 'do whatever';
unlock();
Yeah. I still don't understand Perl-style for booleans. Perhaps a kind strange will clarify a more idiomatic way to write the above code. I'd certainly appreciate it if they did1.
In my lame defense, I did go questing about
for clarification on Perl booleans, but mostly the
answers seemed to be that booleans in Perl are
undefined
and everything else. Is that as
deep as the answer goes?↩