16.21.3. Discussion
The alarm function takes one argument: the integer
number of seconds before the kernel sends your process a
SIGALRM, that is, an alarm signal. It may be
delivered after that time in busy time-sharing systems. The default
action for SIGALRM is to terminate your program,
so you should install your own signal handler.
Because this example should work no matter what operation is being
timed out, we take some special precautions in case your long-running
operation contains a slow syscall. Slow syscalls are those that don't
return immediately, but await some external event, such as for I/O to
happen or some sort of timer to go off. These external events include
read (including readline, the
<FH> operator), write,
and open on certain devices, fifos, and sockets,
as well as accept, connect,
send, recv,
flock, wait,
waitpid, and of course, sleep.
If the alarm hits while you're in a slow syscall and you simply catch
the signal and return, you'll go right back into that syscall. That's
because Perl automatically restarts syscalls where it's able to. The
only way out of them is to raise an exception through
die and then let eval catch it.
(This works because the exception winds up calling the C library's
longjmp(3) function, which is what really gets
you out of the restarting syscall.)
The nested exception trap is because you cannot be sure that
arbitrary code in your long-running operation isn't going to raise
some exception of its own. If it did, then that would pop you out of
the inner eval with the alarm still pending. You
need to make sure to clear the alarm anyway. The second
alarm 0 is in case the signal comes in after
running the long-running operation, but before getting to the first
alarm 0. If you don't do that, you would risk a
tiny race condition—but size doesn't matter in race conditions;
they either exist or they don't. And we prefer that they don't.