InfiniteLoop has asked for the wisdom of the Perl Monks concerning the following question:

Greetings Monks,
I have a perl script, which is run every minute by a cron. Sometimes the script may take longer than 1 minute to complete. In such instances I want the next script (started by the cron) to exit.

To achieve this, currently I do a "ps -ax", from perl space, to find if the process is already running, if true then the script quits. This is more of a hack. How can I solve this problem, more gracefully ?

P.S: The script is executed in an Linux environment

Replies are listed 'Best First'.
Re: self detecting script
by merlyn (Sage) on Sep 15, 2005 at 17:49 UTC
Re: self detecting script
by phaylon (Curate) on Sep 15, 2005 at 17:44 UTC
    I'd use a Lockfile.
    As an idea: Open the file for writing, lock it (or stop here, if it is already locked), write your PID in it (I like to know the pid's of running processes) and unlock (and close of course) it when you're done.

    That's just an idea. If someone knows con's about this technique, please tell :)

    ruoso's suggestion seems much more verbose.

    Ordinary morality is for ordinary people. -- Aleister Crowley
      The only problem with this type of an approach is if the guts of your script are long and complex, having multiple exit points or failure modes, such as "do this or die" or "do this or croak". It gets even worse if you are using some other module which has a "do or die" type function. This way your program will exit without cleaning up the lockfile.

      So, the obvious solution to this is to put the lockfile cleanup into an END block. However, even that will not work all the time. For example, if you are using a module which was compiled from an XS and that module takes a core dump, you will leave the lockfile behind.

      All this is to say that lockfiles have their weaknesses. One way to overcome these flaws is by trying to run your program and checking the presence of the lockfile for, say, average duration of script times a factor of 2. This way, if the lockfile still exists, then it must be a stray file which should be deleted so the script can run.

        Won't Perl release open file handles when it exists? Even in a die or croak when the script exists it will lose the lock.

        -- More people are killed every year by pigs than by sharks, which shows you how good we are at evaluating risk. -- Bruce Schneier
        Good points, thanks. I think the problem with dying scripts can be striked by checking the saved PID as ruoso suggested. Well, then one should be sure not to leave zombies, I'd guess.

        Ordinary morality is for ordinary people. -- Aleister Crowley
Re: self detecting script
by ruoso (Curate) on Sep 15, 2005 at 17:45 UTC

    The standard way of doing that is creating a lockfile which contains the pid of the proccess. The proccess will remove the file when exit.

    When checking, the new instance will see if the file exists, and if exists, check if the pid stored in the file is still alive (a kill -0 $pid will do it).

    If, for some reason, the last proccess didn't remove the lockfile, it will be detected and the next proccess will continue, overriding the lockfile.

    daniel
Re: self detecting script
by saintmike (Vicar) on Sep 15, 2005 at 17:52 UTC
Re: self detecting script
by tcf03 (Deacon) on Sep 15, 2005 at 18:18 UTC
    This should work
    die "a horrible death" if ( -f "lockfile"); open LOCK, ">", "lockfile" or die; close (LOCK); ... do some stuff ... unlink("lockfile");
    Ted
    --
    "That which we persist in doing becomes easier, not that the task itself has become easier, but that our ability to perform it has improved."
      --Ralph Waldo Emerson

      Nice race condition you've managed to write there. If you're going to try and roll your own make sure you do it correctly with sysopen and O_WRONLY|O_EXCL. See the docs, specifically perldoc -f sysopen and perldoc perlopentut.

      Probably not ideal, but this is almost a not too bad method when you know that separate instances of the same program will always start not less than a minute apart. There should be no race conditions. But if for some reason the previous instance never unlinks the file, no subsequent instance will run, which may or may not be what you want. Or if you manually start up the process at the same time cron kicks it off, you may run into a race condition. It may be best to open the file unconditionally and then use lock to get an exclusive lock on the file. Or use one of the previously mentioned modules to do that for you.

      I'll also mention that we do that sort of thing alot in our shell scripts, but it's the only solution since we can't call lock from the shell (also perl is not installed on many of our customer's systems). Unfortunately, we also use this method in shell scripts outside of cron where race conditions are possible, and some sort of file lock would be better.

Re: self detecting script
by tweetiepooh (Hermit) on Sep 16, 2005 at 12:18 UTC
    Instead of cron we often use a sub-shell type mechanism. It doesn't do quite what you want but it sort of does.
    #!/bin/sh (while true do <script> sleep 60 done ) &
    This shell will spawn a second shell in memory that will execute your script and once ended (for any reason) restart it in 60 secs. So it's not running on the minute but we find it often does the job.