Hi. I know this thread is almost 12 years old. I still want to clarify the presented problem for any possible new visitors searching for an answer on the subject.
Your previous code, the one that quits "unexpetedly", has issues, which I comment inline:
foreach $j (@j) {
alarm 2;
# ^ You shouldn't set an alarm out of the scope where you eval the
+ time out and localize $SIG{ALRM};
local $SIG{ALRM} = sub { die "timed out" };
# ^ This localization should be inside an eval block so you can ca
+tch the alarm.
eval { callfc() };
# ^ Here, you are only catching errors from your function, not fro
+m a timeout
next if $@ and $@ =~ /^timed out$/;
# ^ You are checking for the exact string "timed out", but you sho
+uld have /^timed out/ (without $ at the end), or, "timed out\n" in bo
+th places (including the new-line character) because otherwise the me
+ssage will include traces information from "die"
}
Now, for your "working" code, which still has issues, I commend inline too:
foreach $j (@j) {
print "Next Loop $j \n";
eval {
local $SIG{ALRM} = sub { die "Alarm\n"; };
alarm(2);
&callfc();
};
# ^ This is much better, you localize $SIG{ALRM} inside eval, and yo
+u are also using
# the new-line character in the message (and that's why it works (pa
+rtially))
# In here, you should have called alarm(0) to cancel the alarm.
# It doesn't matter if you are out of the eval block, the alarm cont
+inues to run
# and you eventually can have an uncatched "timeout" while being on
+a different
# part of your flow. You must call alarm(0) after your eval block (n
+ot from inside
# because it can die for a different cause too and never get to the
+alarm(0) line).
# So, I added it here:
alarm 0;
if ($@) {
die unless $@ eq "Alarm\n";
# ^ You should send the error to the die too so you know what went
+ wrong,
# like this: die $@ unless $@ eq "Alarm\n";
# But yes, you would catch a timeout here (if alarm 0 was present
+too above).
} else {
print "Didnt\n";
}
}
print "I am continuing with the code\n";
# ^ Yeah... except if you didn't set alarm(0) above, and your script c
+ontinues
# to run and take longer than the alarm to complete, you'll eventually
+ have an
# error stating "Terminating on signal SIGALRM(14)" and wonder why tha
+t happened.
So, to summarize, this is what a timeout block should have, explained:
# First, the thing you want to timeout must be inside of an eval block
+:
eval {
# Second, localize the signal handling and set alarm to the desired
+timeout limit:
$SIG{ALRM} = sub { die "timeout\n" };
alarm 10;
# Third, add the lines of code you specifically want to timeout:
do_something_here_that_can_timeout();
};
# Fourth: CANCEL the alarm immediately or you may have problems later!
# This should be AFTER the eval block, not inside the eval block:
alarm 0;
# Fifth: Check for errors to see if you had a timeout or something els
+e:
if ($@ eq "timeout\n") {
# Do whaever you need if a timeout happened
handle_timeout();
} else {
# You had an actual error and not a timeout, so handle it:
handle_error($@); # or just: die $@
}
I hope this helps anyone trying to understand it.
- Zarabozo
|