I have a function which calls several other functions, e.g.
sub do_work {
send_mail();
send_soap_envelope();
send_rpc();
}
The called functions may possibly hang, so I want to stop them after some timeout. I want to avoid forking because it is expensive in my context (e.g. database handles need to be recreated after each fork). I’ve come up with the following scheme:
sub timeout {
my ($code) = @_;
eval {
alarm 2;
local $SIG{ALRM} = sub { die 'timeout' };
&$code;
alarm 0;
};
# handling of $@ eq 'timeout' removed for brevity
}
sub do_work {
timeout \&send_mail;
timeout \&send_soap_envelope;
timeout \&send_rpc;
};
The timeout() function (in this example hardcoded to a timeout of 2 seconds) uses an eval block as a means of aborting the execution of the payload function using die.
This works fine in my test scenarios, but I’m feeling uneasy about what will happen if the die interrupts the payload function while the Perl interpreter is not in a “safe state”, e.g. while it is processing an XS subroutine. Is my gut feeling right?
Since 5.8.1, Perl uses “safe signal handling”. It doesn’t give your signal handler to the system, it gives a safe signal handler instead. This safe signal handler simply notes that a signal was received and returns. Between executing Perl opcodes, the interpreter checks if any signals were received and calls your signal handler if there was.
That means that signals will not get processed in the middle of a long op, such as a long XS call or a long regex match. Signals interrupt most system calls, so your signal handler will be called shortly after the signal comes in ever if you are in the middle of a blocking system call (e.g.
sleep,read, etc)* — In order to speed up programs, it’s checked a little less often now, but you shouldn’t notice the difference.