As great as the Camel book is, I think that the code and discussion on page 417 are both a bit off. The code has a rather nasty bug, and the race condition described isn't the race condition defended against.

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:

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 }
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.

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:

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 }
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.)

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.


In reply to Re: Re: Re: how can I timeout a function like "read" when reading from a socket by fizbin
in thread how can I timeout a function like "read" when reading from a socket by Goatee

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.