Wait for background processes to complete

C

comp.llang.perl.moderated

Thanks Xho - I've removed the signal handler, but it seems wait always
returns -1 so - the loop is a nop? Where in the code should it go?

I'll pipe in here since the 'quick 'n dirty' solution
was mangled and diss'ed.

The safest action is an asynchronous wait with a
tight loop in the handler (perldoc perlipc):

use POSIX ":sys_wait_h";
$SIG{CHLD} = \&REAPER;

# now do something that forks...
...

sub REAPER { 1 while waitpid(-1, WNOHANG)) > 0; }
 
G

grocery_stocker

[please trim your quotations]

OK - would you have a good example of the fork-system-waitpid method -
not the same one that's in all the other posts or the camel book?

It's a more efficient use of everybody's time for you to use IPC::Run,
which has been written and tested by someone who understands these
issues and is prepared to solve them properly and portably, than it is
for a random Usenaut to provide you with a code snippet.

However, off the top of my head, completely untested, probably not
portable to Win32 or other non-POSIX systems, etc. etc.,

use POSIX qw/WNOHANG/;

{
my %kids;

$SIG{CHLD} = sub {
my ($pid, @died);
push @died, $pid while $pid = waitpid -1, WNOHANG;
delete @kids{@died};
};

sub background {
my (@cmd) = @_;

defined (my $pid = fork)
or die "can't fork for '$cmd[0]': $!";

if ($pid) {
$kids{$pid} = 1;
return;
}
else {
local $" = "' '";
exec @cmd or die "can't exec '@cmd': $!";
}
}

sub finish {
waitpid $_, 0 for keys %kids;
%kids = ();
}
}

while (<*.txt>) {
print "Copying $_\n";
background cp => $_, "$_.old";
}

finish;

This will break if used in conjunction with system or anything else that
relies on SIGCHLD, and an OO person would probably say it should be
implemented as an object. Use IPC::Run.

Ben

I really don't grasp the significance of having $kids{$pid} equal 1.
Can some enlighten me o this?
 
B

Ben Morrow

Quoth grocery_stocker said:
I really don't grasp the significance of having $kids{$pid} equal 1.
Can some enlighten me o this?

It's one of the standard idioms for using a hash as a set. Every time we
create a child, we add an entry to the hash; every time one dies on its
own, we delete its entry. Then at the end we can use keys %pids to
retrieve the list of pids we still need to wait for. The only thing that
matters about %kids are its keys: we never use the values, so they can
be set to anything. I prefer using 1 since then the values are all true;
you can get slightly better memory use with

$kids{$pid} = ();

which inserts the key but doesn't create a value for it at all, but then
you have to test with exists, which I find annoying. Since in this case
I don't test for existance of keys at all, this doesn't matter: using 1 is
just a habit.

Ben
 
P

pgodfrin

I'll pipe in here since the 'quick 'n dirty' solution
was mangled and diss'ed.

The safest action is an asynchronous wait with a
tight loop in the handler (perldoc perlipc):

use POSIX ":sys_wait_h";
$SIG{CHLD} = \&REAPER;

# now do something that forks...
...

sub REAPER { 1 while waitpid(-1, WNOHANG)) > 0; }

Hi Charles - still doesn't wait - in fact the REAPER subroutine never
even gets called - I'm beginning to get back the 'flaw' concept...

pg
 
N

nolo contendere

Hi Charles - still doesn't wait - in fact the REAPER subroutine never
even gets called - I'm beginning to get back the 'flaw' concept...

What's wrong with Xho's code earlier in the thread?

$ perl -wle 'use strict; fork or exec "sleep " . $_*3 foreach 1..3 ;
\
my $x; do {$x=wait; print $x} until $x==-1'
 
P

pgodfrin

What's wrong with Xho's code earlier in the thread?

$ perl -wle 'use strict; fork or exec "sleep " . $_*3 foreach 1..3 ;
\
my $x; do {$x=wait; print $x} until $x==-1'

You know what mon? Xho's program does indeed work in parallel AND
wait. Which means I really don't understand how to do this forking
( tee-hee) nonsense. I need to fix the flaw in MY architecture...
pg
 
P

pgodfrin

Gentle persons,

Xho was right from the start. Apparently all the samples from the
camel book, documentation for fork(), wait() and waitpid() are at the
least misleading - at the worse poorly written.

To begin with, if following the camel book's sample:

if ($pid=fork) { # parent here
} elsif { #child here
} # ...

One can indeed fork - but - the wait loop simply doesn't wait because
it returns -1 upon the first iteration.

The rest of the discussions in perliupc are quite interesting, but
useless in solving my problem here (although I did learn about signal
handlers a little bit).

In sum total here is the answer. Many, many thanks to Xho who was
right from the very beginning. Shame on the Perl documentation for
unnecessary obfuscation.

@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
print "Copying $_ \n";
fork or exec("cp $_ $_.old") ;
}
do {$x=wait;} until $x==-1 ;
#{1 until -1==wait;}
print "\n<<<<< End of exercise >>>>>\n";
exit;

Incidentally Xho's other suggestion {1 until -1==wait; } also work.
pg
 
P

pgodfrin

Gentle persons,

Xho was right from the start. Apparently all the samples from the
camel book, documentation for fork(), wait() and waitpid() are at the
least misleading - at the worse poorly written.

To begin with, if following the camel book's sample:

if ($pid=fork) { # parent here

} elsif { #child here
} # ...

One can indeed fork - but - the wait loop simply doesn't wait because
it returns -1 upon the first iteration.

The rest of the discussions in perliupc are quite interesting, but
useless in solving my problem here (although I did learn about signal
handlers a little bit).

In sum total here is the answer. Many, many thanks to Xho who was
right from the very beginning. Shame on the Perl documentation for
unnecessary obfuscation.

@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
print "Copying $_ \n";
fork or exec("cp $_ $_.old") ;}

do {$x=wait;} until $x==-1 ;
#{1 until -1==wait;}
print "\n<<<<< End of exercise >>>>>\n";
exit;

Incidentally Xho's other suggestion {1 until -1==wait; } also work.
pg

and wiat; all by itself also works now...

sheesh!
 
X

xhoster

pgodfrin said:
Gentle persons,

Xho was right from the start. Apparently all the samples from the
camel book, documentation for fork(), wait() and waitpid() are at the
least misleading - at the worse poorly written.

To begin with, if following the camel book's sample:

if ($pid=fork) { # parent here
} elsif { #child here
} # ...

One can indeed fork - but - the wait loop simply doesn't wait because
it returns -1 upon the first iteration.

I suspect you are misinterpreting something. (For one thing, there is
no "wait loop" in what you have shown!) The code above should be
equivalent to my "fork or exec" code as long as the child block of the "if"
has an exec or a exit, so that the child itself it doesn't fall through
into the invisible wait loop.

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

I suspect you are misinterpreting something. (For one thing, there is
no "wait loop" in what you have shown!) The code above should be
equivalent to my "fork or exec" code as long as the child block of the "if"
has an exec or a exit, so that the child itself it doesn't fall through
into the invisible wait loop.

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.

Hi Xho,
OK - I didn't include the wait loop in the snippet simply for brevity.
I tried using the if logic and it just doesn't wait or fork properly.
Since I'm looping though filenames, this is inadequate. Unless proven
otherwise - I'll say the Camel book code is unclear and incorrect for
my purposes:

@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
if($pid=fork)
{
print "Copying $_ to $_.old\n";
exec("cp $_ $_.old") ;
} elsif (defined $pid) { exit; }
} # end loop
wait;

print "\n<<<<< End of exercise >>>>>\n";
exit;

However, this works AND waits. But - this is very interesting - the
last file in the sample list is 3 or 4 times larger than the first few
files, which permits me to show that the wait does indeed wait for
child processes, but since the first fork replaces the parent process,
the wait command no longer has children to wait on and then continues
to the next statement, while the last cp command is still running.

@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
print "Copying $_ to $_.old\n";
fork or exec("cp $_ $_.old") ;
} # end loop
wait;
print "\n<<<<< End of exercise >>>>>\n";
exit;

But - that's no good - which necessitates the loop you had suggested
from the start. Once again Xho - you da MAN!

@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
print "Copying $_ to $_.old\n";
fork or exec("cp $_ $_.old") ;
} # end loop
do {$x=wait; print "$x\n"} until $x==-1 ;
print "\n<<<<< End of exercise >>>>>\n";
exit;
 
P

Peter J. Holzer

You may have been mislead, but please tell us what aspect of the
documentation mislead you.


Compare this with your script:
@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
if($pid=fork)
{
print "Copying $_ to $_.old\n";
exec("cp $_ $_.old") ;
} elsif (defined $pid) { exit; }
} # end loop
wait;

print "\n<<<<< End of exercise >>>>>\n";
exit;

However, this works AND waits.

No, it doesn't. It doesn't work and it doesn't wait.
But - this is very interesting - the
last file in the sample list is 3 or 4 times larger than the first few
files, which permits me to show that the wait does indeed wait for
child processes, but since the first fork replaces the parent process,
the wait command no longer has children to wait on and then continues
to the next statement, while the last cp command is still running.
@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
if($pid=fork)
{

Here we are in the parent process ($pid != 0).
print "Copying $_ to $_.old\n";
exec("cp $_ $_.old") ;

You exec the cp command. This means that the parent process will now
execute "cp" instead of your script - after doing this it will exit.
} elsif (defined $pid) {

Here we are in the child process.

You exit immediately, so the child process does absolutely nothing. Why
fork at all if you don't want your child process to do anything?

In any case, neither process will reach the end of the loop. So it will
copy exactly one file and
} # end loop

since it never gets here it won't wait.

You need to exec your program in the *child* process:


@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
if($pid=fork)
{
# parent - do nothing
} elsif (defined $pid) {
# child
print "Copying $_ to $_.old\n";
exec("cp", $_, "$_.old") ;

# we won't get here if exec worked:
die "exec cp failed: $!";
}
} # end loop

# now wait for all the children:
do {$x=wait; print "$x\n"} until $x==-1 ;
 
P

Peter J. Holzer

I'll pipe in here since the 'quick 'n dirty' solution
was mangled and diss'ed.

The safest action is an asynchronous wait with a

Before 5.8.0 that was actually unsafe. But while it is now safe in perl
5.8.x (and 5.10.x), it still has the tiny flaw of absolutely *not* doing
what the OP wants. 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.

hp
 
X

xhoster

pgodfrin said:
Hi Xho,
OK - I didn't include the wait loop in the snippet simply for brevity.

A little too much brevity often leads to many cycles of posting, which is
anything but brief!
I tried using the if logic and it just doesn't wait or fork properly.
Since I'm looping though filenames, this is inadequate. Unless proven
otherwise - I'll say the Camel book code is unclear and incorrect for
my purposes:

Peter has already described what went wrong here--you reversed the
parent and child roles in your code (which I snipped). If there is a
specific part of the perldoc that mislead you into doing that, let's figure
it out what that was and have it fixed. (The Camel book, I don't know how
to get that fixed so am slightly less interested in it.)

....
However, this works AND waits. But - this is very interesting - the
last file in the sample list is 3 or 4 times larger than the first few
files, which permits me to show that the wait does indeed wait for
child processes, but since the first fork replaces the parent process,
the wait command no longer has children to wait on and then continues
to the next statement, while the last cp command is still running.

I don't know what you meant by "the first fork replaces the parent process"
but whatever you meant I suspect it is heading the wrong way. What the
code below does is wait for one child, presumably whichever child
finishes first. Then it prints and exits, while N-1 other children are
still running. Some shells' "wait" can wait for all children. Perl's
"wait", like C's "wait", does not. It waits for one child.
@fl= (</fausb/sample/*.txt>);
foreach (@fl)
{
print "Copying $_ to $_.old\n";
fork or exec("cp $_ $_.old") ;
} # end loop
wait;
print "\n<<<<< End of exercise >>>>>\n";
exit;

But - that's no good - which necessitates the loop you had suggested
from the start.

The reason the loop works is that instead of just calling wait once, it
calls "wait" over and over until the OS says "Hey bozo, there is nothing
left to wait for". Well, it actually doesn't say that, it just returns -1,
but "Hey bozo..." is what it means.

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

comp.llang.perl.moderated

Before 5.8.0 that was actually unsafe. But while it is now safe in perl

Huh.. just for clarity, here's what I wrote:

use POSIX ":sys_wait_h";
$SIG{CHLD} = \&REAPER;
# now do something that forks...
...
sub REAPER { 1 while waitpid(-1, WNOHANG)) > 0; }

In fact, historically, there's a clear recommendation to tight-loop
exactly as shown because you may lose signals occurring in
near concurrency if you don't. Or are you
suggesting that an explicit wait on each of
the child processes would somehow be safer...
5.8.x (and 5.10.x), it still has the tiny flaw of absolutely *not* doing
what the OP wants. 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.
You're right, a asynchronous wait would need to
save child pids and loop until they're reaped.
I believe a such a solution was already shown.
 
G

grocery_stocker

Quoth grocery_stocker <[email protected]>:





It's one of the standard idioms for using a hash as a set. Every time we
create a child, we add an entry to the hash; every time one dies on its
own, we delete its entry. Then at the end we can use keys %pids to
retrieve the list of pids we still need to wait for. The only thing that
matters about %kids are its keys: we never use the values, so they can
be set to anything. I prefer using 1 since then the values are all true;
you can get slightly better memory use with

$kids{$pid} = ();

which inserts the key but doesn't create a value for it at all, but then
you have to test with exists, which I find annoying. Since in this case
I don't test for existance of keys at all, this doesn't matter: using 1 is
just a habit.

Ben


Okay, why would you have to test for exists if

$kids{$pid} = ();

just creates something like undef.
 
P

Peter J. Holzer

Huh.. just for clarity, here's what I wrote:

use POSIX ":sys_wait_h";
$SIG{CHLD} = \&REAPER;
# now do something that forks...
...
sub REAPER { 1 while waitpid(-1, WNOHANG)) > 0; }

In fact, historically, there's a clear recommendation to tight-loop
exactly as shown because you may lose signals occurring in
near concurrency if you don't.

The problem with this approach is that until 5.8.0 (when "safe signals"
were introduced), the REAPER function would be called as soon as the
signal arrived regardless of what the perl interpreter was doing at the
time - there was a very real risk that this would crash the perl
interpreter (in a real world application which forked about 50,000 to
100,000 times a day, the parent process would crash every few days with
perl 5.6.0). In C the POSIX standard explicitely states which functions
are safe inside a signal handler (and the rest has to be considered
unsafe), but for perl no such list existed (so everything has to be
considered unsafe). In perl 5.8.0, the "real" signal handler only notes
that a signal arrived, and perl will than invoke sub REAPER when it can
safely do this (which may be quite some time later).

Or are you suggesting that an explicit wait on each of the child
processes would somehow be safer...

Avoiding signal handles was indeed "somehow safer" before 5.8.0,
because signal handlers were fundamentally unsafe (and arguably broken)
in perl.

But that was only a side note. My real argument was that this code has a
completely different purpose: It "reaps" children as soon as they exit
to avoid zombies. This is useful for long-running server processes which
don't really want to wait for their children - they just want to fire
them off and then forget about them. But the OP explicitely wants to
wait for his children so that is what he should do. There is no need for
any signal handlers - they just add complexity which isn't needed and
obscure the purpose of the code.

You're right, a asynchronous wait would need to
save child pids and loop until they're reaped.

Yes. But why would you want to do that if it can be done a lot simpler
and more straightforward?

hp
 
P

Peter J. Holzer

Okay, why would you have to test for exists if

$kids{$pid} = ();

just creates something like undef.

Consider:

#!/usr/bin/perl
use warnings;
use strict;

my %kids;

$kids{3} = ();
$kids{5} = ();

for (1 .. 9) {
print "$_\n" if is_kid($_);
}

sub is_kid {
return exists($kids{$_[0]});
}
__END__
3
5

Please find an implementation for sub is_kid which doesn't use exists.

hp
 
C

comp.llang.perl.moderated

The problem with this approach is that until 5.8.0 (when "safe signals"
were introduced), the REAPER function would be called as soon as the
signal arrived regardless of what the perl interpreter was doing at the
time - there was a very real risk that this would crash the perl
interpreter (in a real world application which forked about 50,000 to
100,000 times a day, the parent process would crash every few days with
perl 5.6.0). In C the POSIX standard explicitely states which functions
are safe inside a signal handler (and the rest has to be considered
unsafe), but for perl no such list existed (so everything has to be
considered unsafe). In perl 5.8.0, the "real" signal handler only notes
that a signal arrived, and perl will than invoke sub REAPER when it can
safely do this (which may be quite some time later).


Avoiding signal handles was indeed "somehow safer" before 5.8.0,
because signal handlers were fundamentally unsafe (and arguably broken)
in perl.

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. Something like
'waitpid', which is on POSIX's safe list,
in a simple loop was unlikely to cause a
problem.

But that was only a side note. My real argument was that this code has a
completely different purpose: It "reaps" children as soon as they exit
to avoid zombies. This is useful for long-running server processes which
don't really want to wait for their children - they just want to fire
them off and then forget about them. But the OP explicitely wants to
wait for his children so that is what he should do. There is no need for
any signal handlers - they just add complexity which isn't needed and
obscure the purpose of the code.



Yes. But why would you want to do that if it can be done a lot simpler
and more straightforward?

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. To me the solutions
are very close modulo the signal function setup. Even outside the
daemon
setting, I like the signal solution which handles
the reaping as soon as it occurs and bundles child cleanup neatly in a
separate sub.
 
P

pgodfrin

Gentle Persons,
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 begin with, I apologize for my last post (the long one) - upon re-
reading it my language was not clear. I should have said "the
following code works" as opposed to simply "this works AND waits". I
was referring to the code below that sentence. Sorry.

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 is http://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.

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. 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? So, thank you Peter for clearing that up. To be fair,
the only place where the "if" construct is indeed placed on paper (or
virtual paper) is the camel book - but it is also not clear where the
code should be placed. But clearly, that is clear now :).

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

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:

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

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, unlike the code same in the
waitpid() function.

In closing, it is perhaps non-intuitive to me that a fork process
should have the child section actually executing the code, but I ask
you how one can intuit that from the sample in the camel book and the
samples in the http://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!

All in all, with the collective help of y'all I been able to
successfully accomplish my goal, but man it was an uphill battle that
could have been very easily solved with better documentation. But -
what I can't figure out is why I had to embark on this journey in a
new posting - has no-one needed to do this before? Oh well...

so long and thanks for all the fish,
pg
 
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. [...]
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 is http://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 the http://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
 

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

Forum statistics

Threads
473,763
Messages
2,569,563
Members
45,039
Latest member
CasimiraVa

Latest Threads

Top