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

Greetings ye Monks.

I have a loop that sleeps at the end and I want to make the thing bail out if the whole loop takes too long. The obvious solution:

alarm 10 while ( ! $done ) { $done = is_it_done_yet(); sleep 1; }

... is bad because perlfunc says:

It is usually a mistake to intermix "alarm" and "sleep" calls. ("sleep" may be internally implemented in your system with "alarm")

So what's the "right" way to do this?

Do I want to fork off a watcher process? Is there a simpler way? If there isn't a simpler way, is there a good CPAN module that handles this?

For background: I'm writing an apache log rotater. I want to compress the files once apache has stopped writing to them, which is what is_it_done_yet() is analogous to. My script checks to see if it can start compressing by using lsof (Unix::Lsof) and compresses them using gzip if it can. If lsof or gzip wander off and never return, I want to have some way to abort the whole thing.

Thanks!
--Pileofrogs

Replies are listed 'Best First'.
Re: Alternatives to Mixing Alarm and Sleep
by ruzam (Curate) on Feb 20, 2010 at 01:39 UTC

    If you're looping with a sleep (instead of actually hanging on a blocking function to return) then I think you just want to just do away with the alarm all together and keep a loop counter.

    $loop_count = 10; while( ! $done and $loop_count-- ) { $done = is_it_done_yet(); sleep 1; }

    The alarm is more useful if you have to break out of a blocking function, in which case you probably wouldn't be using sleep anyway.

    Update...

    On re-read, it sounds like you don't want to get caught hanging on is_it_done_yet(). In which case I think you want to alarm this function separately in an eval.

    while( ! $done ) { eval { local $SIG{ALRM} = sub { die "alarm\n" }; alarm 10; $done = is_it_done_yet(); alarm 0; }; last if @$ and $@ eq "alarm\n"; # hit alarm timeout sleep 1; }
Re: Alternatives to Mixing Alarm and Sleep
by johngg (Canon) on Feb 20, 2010 at 00:21 UTC

    Perhaps you could use select?

    alarm 10; while ( ! $done ) { $done = is_it_done_yet(); select undef, undef, undef, 1.0; }

    I hope this is helpful.

    Cheers,

    JohnGG

Re: Alternatives to Mixing Alarm and Sleep
by zentara (Cardinal) on Feb 20, 2010 at 11:54 UTC
    is there a good CPAN module that handles this?

    Yes, they are called event-loop systems. See POE or Glib

    See Roll your own Event-loop for an example of Glib. POE comes with a cookbook of examples. You want to setup a timer.


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku