6.10 Handling Server Timeout Cases and Working with $SIG{ALRM}
Similar to the case where a user aborts
the script execution by pressing the Stop button, the browser itself
might abort the script if it hasn't returned any
output after a certain timeout period (usually a few minutes).
Sometimes scripts perform very long operations that might take longer
than the client's timeout.
This can happen when performing full searches of a large database
with no full search support. Another example is a script interacting
with external applications whose prompt reponse time
isn't guaranteed. Consider a script that retrieves a
page from another site and does some processing on it before it gets
presented to the user. Obviously, nothing guarantees that the page
will be retrieved fast, if at all.
In this situation, use
$SIG{ALRM}
to prevent the timeouts:
my $timeout = 10; # seconds
eval {
local $SIG{ALRM} =
sub { die "Sorry, timed out. Please try again\n" };
alarm $timeout;
# some operation that might take a long time to complete
alarm 0;
};
die $@ if $@;
In this code, we run the operation that might take a long time to
complete inside an eval block. First we initialize
a localized ALRM
signal handler, which resides inside
the special %SIG hash. If this handler is
triggered, it will call die( ), and the
eval block will be aborted. You can then do what
you want with it—in our example, we chose to abort the
execution of the script. In most cases, you will probably want to
report to the user that the operation has timed out.
The actual operation is placed between two alarm(
) calls. The first call starts the
clock, and the second cancels it. The clock is running for 10 seconds
in our example. If the second alarm( ) call
doesn't occur within 10 seconds, the
SIGALRM signal is sent and the handler stored in
$SIG{ALRM} is called. In our case, this will abort
the eval block.
If the operation between the two alarm( )s
completes in under 10 seconds, the alarm clock is stopped and the
eval block returns successfully, without
triggering the ALRM handler.
Notice that only one timer can be used at a given time.
alarm( )'s returned value is the
amount of time remaining in the previous timer. So you can actually
roughly measure the execution time as a side effect.
It is usually a mistake to intermix alarm( ) and
sleep( ) calls. sleep(
) may be internally implemented in your system with
alarm( ), which will break your original
alarm( ) settings, since every new alarm(
) call cancels the previous one.
Finally, the actual time resolution may be imprecise, with the
timeout period being accurate to plus or minus one second. You may
end up with a timeout that varies between 9 and 11 seconds. For
granularity finer than one second, you can use
Perl's four-argument version of select(
), leaving the first three arguments undefined. Other
techniques exist, but they will not help with the task in question,
in which we use alarm( ) to implement timeouts.
|