(Newbie) Timed operations with eval/die

Discussion in 'Perl Misc' started by Nitzan Shaked, Jun 27, 2005.

  1. Hi all

    I need to do a timed operation: ie give up after some time if it does
    not finish. From what I can gather the "canon" way of doing this is:

    eval {
    local $SIG{ALRM} = sub { die "alarm\n"; };
    alarm 10; ## for example
    $result = my_operation();
    alarm 0;
    };
    if ( $@ ) {
    die unless $@ eq "alarm\n";
    ## CODE IF TIMED-OUT
    } else {
    ## CODE IF DID NOT TIME OUT
    }

    However, it strikes me as if there's a race here, and I can't find
    proof to the contrary in the documentation:

    ==> What happens if my_operation() is performed, finishes on time, even
    $result is set, ... BUT the alarm expires (is set) before running
    "alarm 0", that is: "between" the lines "$result=..." and "alarm 0;".

    In that case the operation was performed but I will not know that, or
    think that it was not.

    Specifically, I want to spawn child processes and wait for them, but
    not more than "x" seconds. Is there a better way to do this other than
    fork() and then:
    1) Have the parent wait for it's children?
    2) Have the children call exec()?

    Of course I realize the race in my case is not terrible: worst case the
    parent will try to kill a PID which was already "reaped". If that
    doesn't happen before pid's overflow and start back from 1 (and it
    won't in reality...) I am safe.

    Still, is there no safe way of doing this?
     
    Nitzan Shaked, Jun 27, 2005
    #1
    1. Advertising

  2. Nitzan Shaked

    Guest

    "Nitzan Shaked" <> wrote:
    > Hi all
    >
    > I need to do a timed operation: ie give up after some time if it does
    > not finish. From what I can gather the "canon" way of doing this is:
    >
    > eval {
    > local $SIG{ALRM} = sub { die "alarm\n"; };
    > alarm 10; ## for example
    > $result = my_operation();
    > alarm 0;
    > };
    > if ( $@ ) {
    > die unless $@ eq "alarm\n";
    > ## CODE IF TIMED-OUT
    > } else {
    > ## CODE IF DID NOT TIME OUT
    > }
    >
    > However, it strikes me as if there's a race here, and I can't find
    > proof to the contrary in the documentation:


    Yes, there is a race condition there.

    >
    > ==> What happens if my_operation() is performed, finishes on time, even
    > $result is set, ... BUT the alarm expires (is set) before running
    > "alarm 0", that is: "between" the lines "$result=..." and "alarm 0;".


    Only you know what happens in that case. Mabye 10 seconds of CPU are
    wasted, and nothing more. Maybe your double-entry book keeping doesn't
    come out balanced. Maybe planes fall out of the sky. I certainly hope it
    is the first one and not one of the latter two.

    > In that case the operation was performed but I will not know that, or
    > think that it was not.


    Yes. If this is merely a waste of CPU time, then I'd say forget it, it
    isn't worth trying to recover that wasted time (unless it simply a matter
    of changing the sleep time to a more optimal value.) If not knowing
    whether or not it finished has more serious consequences, then you need to
    come up with an independent way of knowing whether or not it finished.
    Transactional databases come to mind.


    > Specifically, I want to spawn child processes and wait for them, but
    > not more than "x" seconds. Is there a better way to do this other than
    > fork() and then:
    > 1) Have the parent wait for it's children?
    > 2) Have the children call exec()?


    This doesn't do what you want at all. Where is the "x" seconds part?

    Do you want to do the fork and exec because that is what you wanted
    in the first place, or because you think that doing so will circumvent the
    race condition?

    >
    > Of course I realize the race in my case is not terrible: worst case the
    > parent will try to kill a PID which was already "reaped". If that
    > doesn't happen before pid's overflow and start back from 1 (and it
    > won't in reality...) I am safe.
    >
    > Still, is there no safe way of doing this?


    I doubt there is an absolutely safe way of doing this in pure Perl.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
     
    , Jun 27, 2005
    #2
    1. Advertising

  3. "Nitzan Shaked" <> writes:

    > Thanks Jim and Xho. After posting I realized that I mis-phrased my
    > question:
    > there is indeed a race condition, and just as Jim pointed out there is
    > no real
    > difference between 1uSec before end of my_operation() and 1uSec after.


    OK, no context *and* broken wrapping. That tears it.

    Google Groups, this one's for you: *PLONK*.

    sherm--
     
    Sherm Pendley, Jun 27, 2005
    #3
  4. Thanks Jim and Xho. After posting I realized that I mis-phrased my
    question:
    there is indeed a race condition, and just as Jim pointed out there is
    no real
    difference between 1uSec before end of my_operation() and 1uSec after.

    However, there are cases in which it *is* important to know whether the
    operation finishes or not. And maybe it is not I who wrote operation(),
    and
    maybe it is even a system call.

    What happens if I call wait() in such a manner? I will not know if I
    reaped
    my child process or not, and whether or not to call another wait. For
    that
    matter, I will not even know what to expect of another wait.

    As for Xho's question -- I am forking and exec'ing because I want to
    have
    child processes and don't want any of them to run for more than "x"
    secs.
    I figured the parent will fork, get the children's pids, and wait() on
    them
    with a timed wait (eval/die). The children will hapilly exec whatever
    it is they
    want to exec. ==> Is there a better way? (I mean: of course there is,
    what
    is it? :)
     
    Nitzan Shaked, Jun 27, 2005
    #4
  5. Nitzan Shaked

    Guest

    "Nitzan Shaked" <> wrote:
    > Thanks Jim and Xho. After posting I realized that I mis-phrased my
    > question:
    > there is indeed a race condition, and just as Jim pointed out there is
    > no real
    > difference between 1uSec before end of my_operation() and 1uSec after.
    >
    > However, there are cases in which it *is* important to know whether the
    > operation finishes or not. And maybe it is not I who wrote operation(),
    > and
    > maybe it is even a system call.


    It doesn't matter whether operation was originally written by you or not.
    What matters is whether you can change it. If not, then I think you are in
    for a world of pain if you demand such highly tuned performance.

    If it is a system call, then being interupted with an alarm might cause it
    to either return a distinctive value, or set $! to a distinctive value.
    You need to read about it and test it specifically on your system.

    >
    > What happens if I call wait() in such a manner?


    Which manner?


    > I will not know if I
    > reaped
    > my child process or not, and whether or not to call another wait.


    Maybe "waitpid" is what you are looking for? Although I think you are
    thinking about this wrong. If the sub-program does something which it is
    important to know whether it finished or not, then you need to know whether
    that particular thing finished, not whether the child itself finished, or
    whether the child was waited upon by the parent.

    > For
    > that
    > matter, I will not even know what to expect of another wait.


    perl -le 'fork or exit; print wait; print $!; print wait; print $!'

    >
    > As for Xho's question -- I am forking and exec'ing because I want to
    > have
    > child processes and don't want any of them to run for more than "x"
    > secs.
    > I figured the parent will fork, get the children's pids, and wait() on
    > them
    > with a timed wait (eval/die). The children will hapilly exec whatever
    > it is they
    > want to exec. ==> Is there a better way? (I mean: of course there is,
    > what
    > is it? :)


    I think it would be better for the parent to wait unconditionally and for
    the child to time itself out, rather than for the parent to time out the
    child. Of course if the child is not something you can control, then that
    wouldn't work.

    There are modules on CPAN (Time::Out, IPC::Run, Async, something starting
    with Proc that I can't remmeber) that make what you want look cleaner, but
    AFAICT they are doing the same things behind the scenes and thus suffer the
    same race conditions and such.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
     
    , Jun 28, 2005
    #5
  6. In article <>,
    Nitzan Shaked <> wrote:
    >I need to do a timed operation: ie give up after some time if it does
    >not finish. From what I can gather the "canon" way of doing this is:
    >
    >eval {
    > local $SIG{ALRM} = sub { die "alarm\n"; };
    > alarm 10; ## for example
    > $result = my_operation();
    > alarm 0;
    >};
    >if ( $@ ) {
    > die unless $@ eq "alarm\n";
    > ## CODE IF TIMED-OUT
    >} else {
    > ## CODE IF DID NOT TIME OUT
    >}
    >
    >However, it strikes me as if there's a race here, and I can't find
    >proof to the contrary in the documentation:
    >
    >==> What happens if my_operation() is performed, finishes on time, even
    >$result is set, ... BUT the alarm expires (is set) before running
    >"alarm 0", that is: "between" the lines "$result=..." and "alarm 0;".
    >
    >In that case the operation was performed but I will not know that, or
    >think that it was not.


    How about

    if ( $@ && ! defined $result) {

    This assumes that my_operation() never returns undef -- if it might,
    perhaps there is some other "impossible" value you could pre-assign
    to $result and test for. Worst case, it makes the window smaller.

    Gary
    --
    Chaos reigns within.
    Reflect, repent, and reboot.
    Order shall return.
     
    Gary E. Ansok, Jun 28, 2005
    #6
  7. Nitzan Shaked

    Anno Siegel Guest

    <> wrote in comp.lang.perl.misc:
    > "Nitzan Shaked" <> wrote:


    [...]

    > > want to exec. ==> Is there a better way? (I mean: of course there is,
    > > what
    > > is it? :)

    >
    > I think it would be better for the parent to wait unconditionally and for
    > the child to time itself out, rather than for the parent to time out the
    > child. Of course if the child is not something you can control, then that
    > wouldn't work.


    It just might (work). You could set an alarm timer and then exec() the
    other program. The alarm survives exec(). Of course, the other program
    could catch the alarm, or revoke it, you'll never know for sure.

    For a long-term solution I wouldn't recommend this. Even if tests show
    it works now, a later version of the child program may decide otherwise.

    If it's something that must run now, once or a few times, it may be
    worth considering.

    In a similar vein, you can give a process a file lock before exec()ing
    something else. With (very little) luck, the program leaves the file
    descriptor alone, and the lock indicates to the world that the process
    still exists.

    Anno
     
    Anno Siegel, Jun 30, 2005
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Steve
    Replies:
    0
    Views:
    5,340
    Steve
    Jul 1, 2003
  2. Lloyd Sheen

    Die IDE Die

    Lloyd Sheen, Jan 24, 2004, in forum: ASP .Net
    Replies:
    2
    Views:
    434
    Natty Gur
    Jan 26, 2004
  3. Lloyd Sheen

    More Die IDE Die

    Lloyd Sheen, Jan 24, 2004, in forum: ASP .Net
    Replies:
    5
    Views:
    453
    Alvin Bruney
    Jan 25, 2004
  4. Jesus M. Salvo Jr.
    Replies:
    2
    Views:
    4,239
    robert
    Feb 11, 2006
  5. Timed execution in eval

    , Mar 7, 2008, in forum: Python
    Replies:
    9
    Views:
    236
Loading...

Share This Page