tags:

views:

306

answers:

2

I frequently use the following pattern to set an upper bound to the running time of a particular code fragment in Perl:

my $TIMEOUT_IN_SECONDS = 5;
eval {
    local $SIG{ALRM} = sub { die "alarm\n" };
    alarm($TIMEOUT_IN_SECONDS);
    # do stuff that might timeout.
    alarm(0);
};
if ($@) {
    # handle timeout condition.
}

My questions:

  • Is this the right way to do it?
  • Are there any circumstances under which the running time can exceed $TIMEOUT_IN_SECONDS, or is the above method bullet-proof?
+3  A: 

You probably want to look at Sys::SigAction. I haven't used it myself, but it has some glowing reviews.

One thing to watch out for is if "stuff that might timeout" uses sleep or alarm itself. Also, in the error handling code, I assume you're prepared for errors other than a timeout.

cjm
+2  A: 

Take care with signal handling. Perl receives signals asynchronously and they may be lost or interfere with each other if a signal is received while another signal is being handled by the callback.

Event-handling libraries' Win32 support is pretty so-so in Perl (I have to support non-cygwin Win32), so I generally use a simple polling loop for timeouts:

use Time::HiRes qw(sleep);

sub timeout {
  my $timeout = shift;
  my $poll_interval = shift;
  my $test_condition = shift;
  until ($test_condition->() || $timeout <= 0) {
    $timeout -= $poll_interval;
    sleep $poll_interval;
  }
  return $timeout > 0; # condition was met before timeout
}

my $success = timeout(30, 0.1, \&some_condition_is_met);

The sleep timer can be easily made user- or caller-configurable and unless you are doing an extremely tight loop or have multiple callers waiting on the loop (where you can end up with a race or dead lock), it is a simple, reliable, and cross-platform way to implement a timeout.

Also note that the loop overhead will mean that you cannot guarantee that the timeout is observed absolutely. $test_condition, the decrement, garbage collection, etc. can interfere.

Jeff Ober