in reply to Re: Re: how can I timeout a function like "read" when reading from a socket
in thread how can I timeout a function like "read" when reading from a socket
First off, check the Camel book's errata page; that die line will, with the code you posted, never get called! (except in the case of an alarm timeout, which is the one time the OP doesn't want to die)
Using the fix suggested in the errata, you get something like this:
For those of you playing along at home, the race condition in the original code that the Camel book talks about is that the alarm signal could arrive in between completing the code-that-might-timeout (in this case, $line = <$child>) and the alarm 0 call. But if it does, that's OK - alarm doesn't set up signals to be delivered at regular intervals, so this case is handled exactly as if the alarm had been delivered in the middle of whatever code might be timing out.eval { local $SIG{ALRM} = sub { die "alarm timeout" }; alarm $timeout; eval { # operation you're waiting on which might die() }; alarm 0; # cancel the alarm die $@ if $@; }; alarm 0; # race help die $@ if $@ && $@ !~ /alarm timeout/; if ($@) { # Whatever you want on an alarm timeout }
The one thing you really don't want is to have the code leave the outer nested eval without either calling alarm 0 or having the alarm signal delivered. In that case, you run the risk of having the alarm be delivered with no alarm handler in place.
The way that can happen is if some signal (other than SIGALARM) comes in right before the alarm 0 call, and that signal causes things to die. We then exit the outer eval with the alarm still possible.
The astute of you may notice that even with the code from the Camel book there's still a race condition present, though it requires the code-that-might-timeout to almost timeout, and then have another signal delivered right before the first alarm 0 call. This is now verging on the truly ridiculous, though it might still be possible to trigger, depending on how your OS queues signals and on how slow the perl interpreter could become in moving from one statement's actions to the next.
My fix for that race condition is this, which I think now handles all the possible race conditions, and is (IMO) a slight bit prettier than the double-alarm-0 method:
No more nested eval, and it is easy to see that alarm 0 is called for every exit path from the eval block. (Unless you do something crazy like re-assign to $SIG{__DIE__} inside your SIGHUP handler. If you do something like that, you're on your own.)eval { local $SIG{ALRM} = sub { die "alarm timeout" }; local $SIG{__DIE__} = sub { alarm 0; die @_ }; alarm $timeout; # operation you're waiting on which might die() # in the grandparent post, this was # $line=<$child> alarm 0; # cancel the alarm }; die $@ if $@ && $@ !~ /alarm timeout/; if ($@) { # Whatever you want on an alarm timeout }
There's also not a reason to discuss why you set up the $SIG{__DIE__} handler, whereas with the nested-eval-double-alarm-0 you might later have to explain to coworkers the obscure race condition you're avoiding with that second alarm 0 statement. Here, the alarm 0 inside the __DIE__ handler isn't just a race condition guard - it guards all sorts of things. Perhaps race conditions are fun lunch-time conversation at your office, but I find it easier to just code so that the races don't come up in the first place.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Re: Re: Re: how can I timeout a function like "read" when reading from a socket
by Prior Nacre V (Hermit) on Mar 09, 2004 at 08:31 UTC | |
|
Re: Re: Re: Re: how can I timeout a function like "read" when reading from a socket
by Goatee (Novice) on Mar 09, 2004 at 09:54 UTC |