[Chapter 5] 5.6 Using Eval for Time-Outs
Author: Sriram Srinivasan
Date: 1999-07-02T01:32:47Z

#

| Chapter 5 Eval |

## 5.6 Using Eval for Time-Outs

When you call `eval`, Perl makes a note of the next statement to start executing just in case a `die` is invoked somewhere within. Internally, `die` happens to invoke a `longjmp`, so Perl wastes no time at all transferring control back up to the statement following `eval`, regardless of how deep the stack is. (All temporary and local variables created in the `eval`'ed code are automatically garbage collected.)

The use of `setjmp` and `longjmp` internally gives us one new technique: aborting blocked system calls and infinite loops. Let's say you want to wait at most 10 seconds for the user to type something.[4] If you say `$buf` `=` `<>`, the program is blocked until the user deigns to hit a carriage return, but we would like Perl to abort it after waiting 10 seconds. Generating a time-out is not really a problem; the built-in function `alarm()` can be used to generate an `ALRM` signal after a given number of seconds, like this:

    $SIG{ALRM} = \&timed_out;
    alarm(10);      # Tells the OS to issue an ALRM signal after 10 seconds
    $buf = <>;      # Go into a blocking read

> [4] Thanks to Tom Christiansen for this example.

The procedure `timed_out` is called (after 10 seconds) regardless of what Perl happens to be executing at that time, be it a blocked `read` or an infinite loop. The problem is, how does `timed_out` force Perl to abandon what Perl happened to be doing at the time it was called? That's where `eval`/`die` come in. Put an `eval` around `$buf = <>` and a `die` inside `timed_out()`, and control will be restored to the statement following `eval` (the `if` statement), as shown here:

    $SIG{ALRM} = \&timed_out;
    eval {
        alarm (10);
        $buf = <>;
        alarm(0);           # Cancel the pending alarm if user responds.
    };
    if ($@ =~ /GOT TIRED OF WAITING/) {
        print "Timed out. Proceeding with default\n";
        ....
    }

    sub timed_out {
        die "GOT TIRED OF WAITING";
    }

If the user doesn't hit a return within 10 seconds, `timed_out` is called from the signal handler, which calls `die`, which internally `longjmps` over to the statement following the innermost `eval`. If the user does hit a return within the allotted time, `alarm(0)` is called to reset the alarm.

Note that if the alarm goes off, `$@` contains something like "GOT TIRED OF WAITING at foo.pl line 100," so you cannot use `eq`; you must use a regular expression match (or the `index` operator).

Tom Christiansen pointed out a subtle and interesting point. It is essential that you set `alarm` inside the `eval` block, because on a heavily loaded machine (and for small time-out periods), it is possible to lose the time-slice after the call to `alarm` and before it has a chance to enter the protected section (the `eval` block). Later on, when the program regains the time-slice, it is possible that the time-out interval has expired, and the program will abort.

5.5 Using Eval for Efficiency |  | 5.7 Eval in Other Languages