[ Team LiB ] Previous Section Next Section

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.

    [ Team LiB ] Previous Section Next Section