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 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
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. |
|