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…
Leave a comment