There is a number of very good, but not very well known Perl modules on CPAN.

Sometimes I'll be writing short posts about such modules which I use and appreciate.

There is a common task of executing a script from cron periodically, subject to the following conditions:

  • a script can occasionally run a relatively long time (longer than the interval at which it is launched by cron);
  • such long runs do not happen often;
  • running two or more instances of the script at the same time will lead to all sorts of strange things happening and must be avoided;
  • skipping a single run is no big deal.

A usual method to prevent strange things from happening is to use a lockfile, like in this example:

use strict;
use warnings;

use Fcntl qw(:DEFAULT :flock);

sysopen(L, "/var/run/myprocess.lock", O_WRONLY | O_CREAT)
    or die "cannot open lockfile: $!";
flock(L, LOCK_EX | LOCK_NB)
    or die "cannot obtain lock: $!";

# ... do work here ...

unlink "/var/run/myprocess.lock"; # optional
close(L);

(You might want to silently exit when flock fails in order to not spam yourself with useless cron mails).

Nothing fancy, really. But over time it becomes boring to write all this housekeeping code in every little cronjob, especially since in some cases the "do work here" part can be comparable in size to the locking part.

The solution, of course, is to use the CPAN magic and to find a module which sweeps all this complexity under the rug, leaving us with a clean and simple interface, so that we can concentrate on getting the job done.

As usual with CPAN, there is not one, but several modules which were written to perform this task. Most of them are rather powerful, which is unfortunate, since we want simplicity of use above all else. The bells and whistles provided by those modules might be needed in certain situations, but for the purpose described above they just get in the way.

There is, however, a wonderfully simple (and a rather clever) module by Elizabeth Mattijsen, Sys::RunAlone. To get the same functionality as the code above, all you need to do is this:

use strict;
use warnings;
use Sys::RunAlone;

# ... do work here ...

__END__

That's it. Nothing else to write.

There are only two minor things to remember about this module, if you want to avoid problems.

First, it uses the script's DATA handle to do the locking (that is, it actually uses the script's file itself). So if you have several symlinks pointing to the same script, you cannot run them at the same time for it is still one physical file and one DATA handle.

Second, and for the same reason, if you modify the script while it is running and then launch it again, it will fail to detect that another instance is already running, since the DATA handle will be different.

Just keep this in mind when you use it.