(Newbie) Timed operations with eval/die

N

Nitzan Shaked

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?
 
X

xhoster

Nitzan Shaked said:
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
 
S

Sherm Pendley

Nitzan Shaked said:
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--
 
N

Nitzan Shaked

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? :)
 
X

xhoster

Nitzan Shaked said:
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
 
G

Gary E. Ansok

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
 
A

Anno Siegel

[...]
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top