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

What are the best method to wrap perl code in timeout sections? What I'm currently using is:
{ local $SIG{ALRM} = sub { die "alarm timeout" }; alarm $timeout; # Here goes the code that can hang, #for example network connections die $@ if $@ && $@ !~ /alarm timeout/; }

Unfortunatelly this doesn't work that well, especially if you're trying to be generic and you've got no control over the block to protect - this block affects the code inside, for example sleep start behaving erradically, if the code inside rewrites DIE subroutine you can also get in trobule..etc.

I started using Event module, which defines it's own method for timeing out, (they put it in Event::Stats, which is like the first place I would look for it...), but the module is written in C and so is it's timeout ...AFAIK.

Replies are listed 'Best First'.
Re: Reliable Timeout
by gaal (Parson) on Feb 14, 2005 at 13:27 UTC
    You have a few errors in the code you're trying.
    1. You need to wrap the code that might time out in an eval.
    2. The die $@ if $@ && $@ !~ /alarm timeout/; has to come after the eval.
    3. It's recommended, when throwing your own exceptions, to end the string in a newline, e.g. die "timeout\n".

    # example { # this scope is here just to localize the signal handler local $SIG{ALRM} = sub { die "timeout\n" }; alarm $TIMEOUT; eval { # iffy code 1; } or do { die $@ unless $@ eq "timeout\n"; # propagate other exceptions # handle timeout }; alarm 0; # free alarm }
      Actually it is better to not add the newline in a die because if you dont Perl will add the line you died on and a newline.
        Which you don't generally need if you're expecting to catch your own exceptions :)

        If there's a chance the die won't be caught by my own code, I like to use Carp::confess instead of die anyway to get the stack trace.

Re: Reliable Timeout
by sh1tn (Priest) on Feb 14, 2005 at 17:05 UTC
    You might want to take a look at Time::Out module.
      Notice that Time::Out is only a nice frontend for alarm, ie it uses exactly the code I described earlier.

      So it's unreliable. alarm(2) doesn't interrupt blocking I/O on MSWin32, so 'timeout' won't do that either.

      So that's new information, thanks, I only knew about problems coming from using alarm-based functions inside block - like using sleep...

      So to conclude - general solution is the one I described initially, it has known deficences - namely problems with reliability ( thus, there are no solutions to my question about "reliable" way to do that ). On Unix you can expect lost signals, side effects from 'sleep' and 'alarm' used inside the block. On MS you can additionally expect haning on blocking I/O (which is what you're usually trying to detect with timeout).

        So it's unreliable. alarm(2) doesn't interrupt blocking I/O on MSWin32, so 'timeout' won't do that either.

        alarm() is a unix'ism and it works in other languages like C under Unix just fine. Just because it isn't implemented properly (probably by MS) doesn't mean it is broken. Assuming alarm doesn't work for you then you may want to try something like forking a child process, let it perform the work and return the results, and have the parent send it a kill if it doesn't return in a reasonable period of time.

        So I conclude that esteemed perlmonks don't know solution to that problem. pity.

        And you've come to this conclusion by reading through all of the previous posts about this problem? What new issue have you come up with that hasn't been discussed in the past?