Wait for background processes to complete

P

Peter J. Holzer

Hm, I thought I recalled that, even with
Perl's broken pre-5.8 signal handling, the
issue was most likely to surface with op's
not on POSIX's safe list.

One function not on POSIX's safe list is malloc. And since just about
anything in perl is dynamically allocated ...
Something like 'waitpid', which is on POSIX's safe list, in a simple
loop was unlikely to cause a problem.

waitpid probably wasn't a problem. The loop might have been. Simply
calling the reaper function (which needs to allocate a call frame) might
have been. In the program I mentioned, a reaper function deleted an
entry from a hash - this definitely was a problem.

Yes I agree that's probably true here but, as you mention, you'd have
to check for -1 because the process was reaped, stopped, or terminated
by some signal for instance. And, if you're concerned about zombies,
you really need to keep a list of pids to wait on.

Zombies are not a concern in this case. When you post a followup to a
question of an obvious perl beginner, please try to provide a solution
to his problem. A solution to a completely different problem will just
confuse him, especially, if you don't say that it is a solution for a
completely different problem.

To me the solutions are very close modulo the signal function setup.

The signal function setup is the difference, yes. For the OP's problem a
signal handler is not only not needed, but does nothing at all to solve
the problem.

hp
 
C

comp.llang.perl.moderated

One function not on POSIX's safe list is malloc. And since just about
anything in perl is dynamically allocated ...


waitpid probably wasn't a problem. The loop might have been. Simply
calling the reaper function (which needs to allocate a call frame) might
have been. In the program I mentioned, a reaper function deleted an
entry from a hash - this definitely was a problem.



Zombies are not a concern in this case. When you post a followup to a
question of an obvious perl beginner, please try to provide a solution
to his problem. A solution to a completely different problem will just
confuse him, especially, if you don't say that it is a solution for a
completely different problem.

I believe the OP was given a couple of solutions.
Sometimes discussions do segue off in different
directions but the info can be useful at times.
It was to me. And I disagree -- for at least
one of the solutions -- zombies could occur
and need to be dealt with.
The signal function setup is the difference, yes. For the OP's problem a
signal handler is not only not needed, but does nothing at all to solve
the problem.

Again I disagree. A viable alternative solution could make use of a
SIGCHLD handler. But I think
the thread has now outlived its usefulness.
 
P

Peter J. Holzer

I believe the OP was given a couple of solutions.
Sometimes discussions do segue off in different
directions but the info can be useful at times.

True, but there should be some continuity from one posting to the next.
The code in the posting you replied to had a serious problem, but it
didn't produce zombies (Well, one - for a short time). Butting in with a
zombie-avoidance method was a complete non-sequitur. It would have been
ok if you had followed up to an article with code which did actually
produce zombies, or if you had started your article with some indication
that you are not proposing a solution to the problem at hand (something
like "This probably isn't the problem here, but in general ..." works
well).
It was to me. And I disagree -- for at least
one of the solutions -- zombies could occur
and need to be dealt with.

I don't recall seeing any solution which did produce zombies and was
otherwise correct (i.e. met the OP's specification). I can think of
at least two ways to achieve this (both of them wait for all children
but not necessarily in the order they terminate), but I don't agree that
in these cases "zombies need to be dealt with". In the worst case all
zombies will be collected immediately after the last child process
terminated. If the system can deal with $n active processes all
be

Again I disagree. A viable alternative solution could make use of a
SIGCHLD handler.

It could. It's just completely useless.

Assume that the loop which forks off the children registers them in
%kids.

Then the SIGCHLD handler could do something like this:

sub REAPER {
for(;;) {
$kid = waitpid(-1, WNOHANG);
last if $kid <= 0;
delete $kids{$kid};
}
}

So, at the end of the program we just need to loop until %kids is empty:

while (keys %kids) {
}

But that's busy-waiting - it will consume 100 % CPU time. We could sleep
inside the loop, but how long? Until the next child terminates. Well, we
already have a function which does sleep until a child terminates - it's
called wait. So the loop turns into:

while (keys %kids) {
my $kid = wait();
delete $kids{$kid};
}

So now we have a loop at the end which waits for all children, and the
REAPER function has no useful function anymore. So we can delete it.

hp
 
C

comp.llang.perl.moderated

...

It could. It's just completely useless.

Assume that the loop which forks off the children registers them in
%kids.

Then the SIGCHLD handler could do something like this:

sub REAPER {
for(;;) {
$kid = waitpid(-1, WNOHANG);
last if $kid <= 0;
delete $kids{$kid};
}

}

So, at the end of the program we just need to loop until %kids is empty:

while (keys %kids) {

}

But that's busy-waiting - it will consume 100 % CPU time. We could sleep
inside the loop, but how long? Until the next child terminates. Well, we
already have a function which does sleep until a child terminates - it's
called wait. So the loop turns into:

while (keys %kids) {
my $kid = wait();
delete $kids{$kid};

}

So now we have a loop at the end which waits for all children, and the
REAPER function has no useful function anymore. So we can delete it.

That's true. The SIGCHLD handler has no apparent advantage here
unless
you have some critical reason to want immediate signal delivery. I'm
not sure what likely scenarios in a non-daemon setting might benefit
--
maybe someone decides to bump a reaped process count in the handler
in
order to sleep during the fork loop if some threshold is exceeded...
Dunno.

At any rate though, it's always seemed cleaner to me to reap child
processes
as soon as possible...particularly at the bargain basement price of a
POSIX
declaration and 1 or 2-liner handler. Not a big deal either way but I
think
it's worth mentioning as an altenative.
 
P

pgodfrin

This was a lot of fun. I would like to respond to the various
observations, especially the ones about the Perl Documentation being
misleading. [...]
To restate the original task I wanted to solve:
To be able to execute commands in the background and wait for their
completion.
The documentation I am referring to ishttp://perldoc.perl.org/.
If you search on the concept of "background processes" this
documentation points you to the following in the Language reference >
perlipc > Background Process :
<begin quote>
You can run a command in the background with:
system("cmd &");
The command's STDOUT and STDERR (and possibly STDIN, depending on your
shell) will be the same as the parent's. You won't need
to catch SIGCHLD because of the double-fork taking place (see below
for more details).
<end quote>
There is no further reference to a "double fork" (except in the
perlfaq8 and only in the context of zombies, which it says are not an
issue using system("cmd &") ). This is confusing.

I find it more confusing that this seems to be in the section with the
title "Using open() for IPC". I fail to see what one has to do with the
other.

There is a general problem with perl documentation: Perl evolved in the
Unix environment, and a lot of the documentation was written at a time
when the "newbie perl programmer" could be reasonably expected to have
already some programming experience on Unix (in C, most likely) and know
basic Unix concepts like processes, fork(), filehandles, etc. So in a
lot of places the documentation doesn't answer the question "how can I
do X on Unix?" but the question "I already know how to do X in C, now
tell me how I can do it in perl!". When you lern Perl without knowing
Unix first, this can be confusing, because the Perl documentation
generelly explains only Perl, but not Unix.

I am not sure if that should be fixed at all: It's the perl
documentation and not the unix documentation after all, and perl isn't
unix specific, but has been ported to just about every OS.
The documentation for wait(), waitpid() and fork() do not explain that
the executing code should be placed in the "child" section of the if
construct.

Of course not, that would be wrong. There can be code in both (if you
wanted one process to do nothing, why fork?). What the parent should do
and what the child should do depend on what you want them to do. It just
happened that for your particular problem the parent had nothing to do
between forking off children.
Some of the examples in perlipc show code occurring in both
the parent and the child section - so it is still not clear. If it
were, why was I insisting on trying to execute the "cp" command in the
parent section?

I don't know. What did you expect that would do?

Your problem was:

I want a process to gather a list of files. Then, for each file, it
should start another process which copies the file. These processes
should run in parallel. Finally, it should wait for all these processes
to terminate, and then terminate itself.

Even this description makes it rather implicit, that the original
process creates children and then the children do the copying. If you
add the restriction that a process can only wait for its children, any
other solution becomes extremely awkward.

Besides it is exactly the same what your shell script did: The shell
(the parent process) created child processes in a loop, each child
process executed one cp command, and finally the parent waited for all
the children.
Furthermore, while the perlipc section is quite verbose, nowhere is
the code snippet do {$x=wait; print "$x\n"} until $x==-1 or any
variation of that wait() call mentioned.

Again, this is a solution to your specific problem. You said you wanted
to wait for all children, so Xho wrote a loop which would do that. This
is a rather rare requirement - I think I needed it maybe a handful of
times in 20+ years of programming outside of a child reaper function.
There are references to a
while loop and the waitpid() function, but being in the context of a
signal handler and 'reaping' - it is not clear.

This is the one situation where this construct is frequently used. It
can happen that several children die before the parent can react to the
signal - in this case the reaper function will be called only once, but
it must wait for several children. This isn't obvious, so the docs
mention it.

However, in your case you *know* you started several children and you
*want* to wait for all of them, so it's obvious that you need a loop. This
hasn't anything to do with perl, it's just a requirement of your
problem.
So, once again many thanks for the help. I would like to know, Peter,
are you in a position to amend the documentation? Also, the perlfaq8
does come closer to explaining, but it is simply not clear how the
fork process will emulate the Unix background operator (&). So how can
we make this better? How's about this:

fork doesn't "emulate &". fork is one of the functions used by the shell
to execute commands. Basically, the shell does this in a loop:

display the prompt
read the next command
fork
in the child:
execute the command, then exit
in the parent:
if the command did NOT end with &:
wait for the child to terminate

So the Shell always forks before executing commands and it always
executes them in a child process. But normally it waits for the child
process to terminate before displaying the next prompt. If you add the
"&" at the end of the line, it doesn't wait but displays the next prompt
immediately.

(ok, that's a bit too simplistic, but I don't want to go into the arcana
of shell internals here - that would be quite off-topic).
In the perlipc, under background tasks. make the statement - "the call
system("cmd &") will run the cmd in the background, but will spawn a
shell to execute the command, dispatch the command, terminate the
shell and return to the calling program. The calling program will lose
the ability to wait for the process as is customary with the shell
script command 'wait'. In order to both execute one or more programs
in the background and have the calling program wait for the executions
to complete it will be necessary to use the fork() function."

I think that would confuse just about anybody who doesn't have exactly
your problem for exactly the same reason you were confused by the
"double fork". It's very specific and should be clear to anyone who
knows what system does and what a unix shell does when you use "&" - I
suspect that sentence about the double fork was added after a discussion
like this one. But I agree that fork should definitely be mentioned here.
system("cmd &") almost never what you would want to do.
Then in the fork section. Show a simple example like the ones we have
been working with AND show a simple approach to using the wait
function.
Furthermore - add sample code to the wait() and fork()
functions that are simple and realistic,

fork and wait are very simple building blocks which can be combined in a
lot of different ways. Any sample code here can cover only basic
constructs like

my $pid = fork();
if (!defined $pid) {
die "fork failed: $!";
} elsif ($pid == 0) {
# child does something here
# ...
exit;
} else {
# parent does something here
# ...
# and then waits for child:
waitpid($pid, 0);
}

More complex examples should go into perlipc (which needs serious
cleanup, IMHO).
unlike the code same in the waitpid() function.

That code is simple and realistic.
In closing, it is perhaps non-intuitive to me that a fork process
should have the child section actually executing the code,

If you don't want the child process to do anything, why would you create
it in the first place?
but I ask you how one can intuit that from the sample in the camel
book and the samples in thehttp://perldoc.perl.org/. To really drive
the point home, Xho's code:
fork or exec("cp $_ $_.old") ;
do {$x=wait;} until $x==-1 ;
Is STILL not intuitive that the child is executing the code!

True. This code isn't meant to be intuitive. It's meant to be short.
I wouldn't write that in production code, much less in an answer to a
newbie question.

hp

HI Peter,
Thanks - you've been quite fair. The only point I would argue is how
many times you needed to wait for background tasks. I guess the
salient point is - I'm not a systems programmer - so I use Perl like
shell scripting and because I think it's stupid to do if-fi and case-
esac pairs.

'nuff said. Good point about the Perl docs being about Perl and not
Unix...

cheers...
pg

p.s. what's an OP ?
 
P

Peter J. Holzer

This was a lot of fun. I would like to respond to the various
observations, especially the ones about the Perl Documentation being
misleading. [...] [76 lines snipped]
Furthermore, while the perlipc section is quite verbose, nowhere is
the code snippet do {$x=wait; print "$x\n"} until $x==-1 or any
variation of that wait() call mentioned.

Again, this is a solution to your specific problem. You said you wanted
to wait for all children, so Xho wrote a loop which would do that. This
is a rather rare requirement - I think I needed it maybe a handful of
times in 20+ years of programming outside of a child reaper function.
[109 lines snipped - please quote only the relevant parts of the
articles you reply to]
Thanks - you've been quite fair. The only point I would argue is how
many times you needed to wait for background tasks.

I need to wait for background tasks quite often. But usually either I
need to wait for only one of them or some of them, but rarely all of
them. But I don't see how it matters. If you know how to wait for one
process, and you know how to write a loop, it is trivial to wait for all
processes.
I guess the salient point is - I'm not a systems programmer - so I use
Perl like shell scripting and because I think it's stupid to do if-fi
and case- esac pairs.

That's ok. Perl just gives you a lot more flexibility than the shell,
at the price of higher complexity. For example, it would be quite easy
to change your script to do a configurable number of copies in parallel
- this is rather hard to do in the shell.
p.s. what's an OP ?

"original poster": The person who started a thread, i.e. you in this case.

hp
 
X

xhoster

Peter J. Holzer said:
Assume that the loop which forks off the children registers them in
%kids.

Then the SIGCHLD handler could do something like this:

sub REAPER {
for(;;) {
$kid = waitpid(-1, WNOHANG);
last if $kid <= 0;
delete $kids{$kid};
}
}

So, at the end of the program we just need to loop until %kids is empty:

while (keys %kids) {
}

But that's busy-waiting - it will consume 100 % CPU time. We could sleep
inside the loop, but how long? Until the next child terminates. Well, we
already have a function which does sleep until a child terminates - it's
called wait. So the loop turns into:

while (keys %kids) {
my $kid = wait();
delete $kids{$kid};
}

So now we have a loop at the end which waits for all children, and the
REAPER function has no useful function anymore. So we can delete it.

It might be useful in the highly unlikely event that you want to be
spawning enough jobs that you will run out of processes if you don't reap
any until all are spawned. Reaping early ones that exit even as the later
ones are still being spawning could help that (But I still wouldn't use a
sig handler, I'd just put a "waitpid(-1, WNOHANG);" inside the spawning
loop). But anyway, since the spawning is unthrottled, you are just racing
disaster anyway--you have no guarantee that enough will finish in time to
prevent you from running out of processes. So something throttling, like
ForkManager would be the way to go.


Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
X

xhoster

Peter J. Holzer said:
Xho's solution is safe (and was so in all perl
versions and does what the OP wants. Well, almost - wait can return -1
if it is interrupted so one should check $! in addition to the return
value.

Are you sure that that is the case? I figured it would return undef on
error. Upon experimenting (with both >5.8 and <5.8) I found that (on my
system) wait returns neither -1 nor undef due to signals, because it
never returns due to signals. The Perl wait behind the scenes just keeps
recalling the system-level wait either until something is reaped, or until
the program dies, or until a die/eval pair causes it to jump out of the
execution path without actually returning.

Xho

--
-------------------- http://NewsReader.Com/ --------------------
The costs of publication of this article were defrayed in part by the
payment of page charges. This article must therefore be hereby marked
advertisement in accordance with 18 U.S.C. Section 1734 solely to indicate
this fact.
 
P

pgodfrin

This was a lot of fun. I would like to respond to the various
observations, especially the ones about the Perl Documentation being
misleading.
[...] [76 lines snipped]
Furthermore, while the perlipc section is quite verbose, nowhere is
the code snippet do {$x=wait; print "$x\n"} until $x==-1 or any
variation of that wait() call mentioned.
Again, this is a solution to your specific problem. You said you wanted
to wait for all children, so Xho wrote a loop which would do that. This
is a rather rare requirement - I think I needed it maybe a handful of
times in 20+ years of programming outside of a child reaper function.

[109 lines snipped - please quote only the relevant parts of the
articles you reply to]
Thanks - you've been quite fair. The only point I would argue is how
many times you needed to wait for background tasks.

I need to wait for background tasks quite often. But usually either I
need to wait for only one of them or some of them, but rarely all of
them. But I don't see how it matters. If you know how to wait for one
process, and you know how to write a loop, it is trivial to wait for all
processes.
I guess the salient point is - I'm not a systems programmer - so I use
Perl like shell scripting and because I think it's stupid to do if-fi
and case- esac pairs.

That's ok. Perl just gives you a lot more flexibility than the shell,
at the price of higher complexity. For example, it would be quite easy
to change your script to do a configurable number of copies in parallel
- this is rather hard to do in the shell.
p.s. what's an OP ?

"original poster": The person who started a thread, i.e. you in this case.

hp

cool - that is in fact my next goal - to have an option that throttle
how may concurrent tasks...

regards from the OP :)
pg
 
P

Peter J. Holzer

Are you sure that that is the case?

No. It's more a "it does happen in C and it isn't documented so better
add an extra check just in case" kind of reaction.
I figured it would return undef on
error. Upon experimenting (with both >5.8 and <5.8) I found that (on my
system) wait returns neither -1 nor undef due to signals, because it
never returns due to signals. The Perl wait behind the scenes just keeps
recalling the system-level wait either until something is reaped, or until
the program dies, or until a die/eval pair causes it to jump out of the
execution path without actually returning.

Good to know. If this is generally the case (not just on your system) it
would be good if this information could be added to perlfunc.

hp
 
D

Dougie!

Greetings,
Well - I've spent a bunch of time trying to figure this out - to no
avail.

Here's what I want to do - run several commands in the background and
have the perl program wait for the commands to complete. Fork doesn't
do it, nor does wait nor waitpid.

Any thoughts?

Here's a sample program which starts the processes:

while (<*.txt>)
{
print "Copying $_ \n";
system("cp $_ $_.old &") ;
}
print "End of excercise\n";
exit;

I mean if this were a shell program - this would work:

for x in `ls *.txt`
do
print "Copying $_ \n"
cp $_ $_.old &
done
wait

thanks,
pg

I know you're probably looking for something very simple. Amazing how
simple things get really complex.

Take a look at http://poe.perl.org/ - specifically the Job Server in
the Cookbook section.
anding framework for doing stuff like what you are looking to do.

Dougie!!!



POE Rocks... It is an outst
 

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,780
Messages
2,569,611
Members
45,271
Latest member
BuyAtenaLabsCBD

Latest Threads

Top