I’m a huge fan of code reuse, and I tend to trust other people’s public code more than my own private code. After all, if they put effort into making it public, they must have put a lot of thought into it. And more than likely it is probably their speciality. That’s why I don’t (for example) implement my own webservers š Having said that, I’ve reimplemented more wheels than I care to admit. Here is me, providing my own version of identity – a built-in emacs function. How embarrassing. Almost put me off blogging that did.
So, I try to do a bit of due dilligence when I’m thinking about writing a simple thing that someone surely has already done. What I want, is an interprocess mutex. There must be a million ways of doing these things, so I have a quick look on CPAN. Searching for mutex brings up LockFile::NetLock which looks interesting (although slightly paranoid, and I don’t have my own ftp server).
IPC::Semaphore looks like it only supports SysV (i.e. not Windows) and the API is somewhat baroque. Win32::Semaphore has the opposite problem (I’m guessing). I did like the look of POSIX::RT::Semaphore but suspected it wouldn’t install on Windows.
dmake.EXE: Error code 129, while making 'Semaphore.o'
MJP/POSIX-RT-Semaphore-0.05.tar.gz
C:\strawberry\c\bin\dmake.EXE -- NOT OK
Running make test
Can't test without successful make
Running make install
Make had returned bad status, install seems impossible
Yep, I was right. I’ll spare you the results from the other hour I spent googling various things like semaphore and process mutex.
Okay, let’s think about this. Maybe I could write my own simple little thing with a nice API, and if I find a decent module later I can delegate to it, or maybe delegate to Win32::Semaphore on Windows and the SysV version everywhere else. The ACE guys call that a wrapper facade. Does flock work on Windows?
So the plan is to call flock LOCK_EX on a file in the constructor, and then flock LOCK_UN in the destructor in a kinda RAII way. Famous last words, but what else could I possibly need?
package IPC::ProcessMutex;
use Carp;
use Fcntl;
sub import { }
sub new
{
my $class = shift;
my $file = shift;
my $fh;
if (! sysopen($fh, $file, O_CREAT)) {
croak "Unable to sysopen $file";
}
my $self = { handle => $fh };
bless $self, $class;
flock $fh, Fcntl::LOCK_EX;
return $self;
}
sub DESTROY
{
my $self = shift;
if (exists $self->{handle}) {
flock $self->{handle}, Fcntl::LOCK_UN;
close $self->{handle};
delete $self->{handle};
} else {
carp 'DESTROY - handle was not defined';
}
}
1;
And to avoid having to think to hard about whether the code actually works or not, I’ll check it with a little test š
use POSIX;
use FindBin;
use lib "$FindBin::Bin/lib/perl5";
use IPC::ProcessMutex;
sub my_log
{
my $ts = POSIX::strftime('%H:%M:%S', localtime(time()));
print "[ $ts ] : ", @_, "\n";
}
{
my_log 'Getting mutex ...';
my $mutex = IPC::ProcessMutex->new('.filename.lock');
my_log 'Got mutex. Sleeping 10 ...';
sleep 10;
}
my_log 'Released mutex.';
Process 1
jared@win32 $ perl get-mutex.pl
[ 21:37:57 ] : Getting mutex ...
[ 21:37:57 ] : Got mutex. Sleeping 10 ...
[ 21:38:07 ] : Released mutex.
jared@win32 $ perl get-mutex.pl
[ 21:38:15 ] : Getting mutex ...
[ 21:38:17 ] : Got mutex. Sleeping 10 ...
[ 21:38:27 ] : Released mutex.
jared@win32 $
Process 2
jared@win32 $ perl get-mutex.pl
[ 21:38:02 ] : Getting mutex ...
[ 21:38:07 ] : Got mutex. Sleeping 10 ...
[ 21:38:17 ] : Released mutex.
jared@win32 $ perl get-mutex.pl
[ 21:38:24 ] : Getting mutex ...
[ 21:38:27 ] : Got mutex. Sleeping 10 ...
[ 21:38:37 ] : Released mutex.
jared@win32 $
Yes! Mission accomplished as they say. I’m tentatively going to go with that…
Read Full Post »