(e-mail address removed) (Mark Gowans) rudeness spits TOFU in our faces:
[ Rudeness corrected - but remember, Mark, if you think I seem at all
hostile towards you just spat in our faces. ]
[ pseudo-code ]
[...] here's a bit of perl to demonstrate exactly the below..
[ code that doesn't use strict or lexical handles ]
I'd recommend using strict and lexical file handles, and then if you still
need help, then try writing example code that works and demonstrates what
you says it does.
Agreed, using pre-defined file handles would be better, although given
that @LIST is of a non-pre-defined size, I couldn't think of a better
way to ensure that I read from the children in order...
Nobody said anything about pre-defined handles. I think you
miss-understand what we mean by "lexical file handles". Perl doesn't
really have lexically scoped file handles but you can pretend it does.
If something is lexically scoped within a loop then each time through
the loop the same name refers to a different thing.
Actually what happens with lexical filehandles is you get hard
references to semi-anonymous symbols. Don't worry you don't need to
understand that 99% of the time. Just act as if you have lexically
scoped file handles!
You said:
$rVar = "r$item";
$wVar = "w$item";
pipe ($rVar, $wVar);
This uses sybolic handle references.
To use lexically scoped file handled you could have said:
pipe(my($rVar),my($wVar));
Note: This autovivication of handles requires 5.6.1 or later. In
earlier versions you need to initialise the variables to handles in
some way such as:
my $rVar = IO::Handle->new;
Oh, and when we said "use strict" what we really mean is:
Always delare all variables as lexically scoped in the smallest
applicable scope unless there is a reason to do so[1].
Do not use symbolic references unless there is a reson to do so.
Don't use barewords[2].
Using strict is not an end in itself but it will help you to achive
these.
Footnotes to the above:
[1] Note this is not just good advice in Perl - it is good advice in
programming in general. If the language you are using supports the
concept of lexically scoped variables and you've not followed this
advice then any question you ask in comp.lang.* is likely to be seen
as rude. This is because often your problem will result from your
failure to use appropriate scoping - and even if it does not, anyone
who tries to help you would have to waste time looking to see if
problem resulted from your failure to use appropriate scoping.
[2] i.e. do not rely on the fact that if there's no function foo()
then Perl will treat foo as 'foo'.
#!/usr/local/bin/perl5 -w
while (TRUE) {
There is no function called TRUE() in Perl.
You got no error because you "forgot" to use strict.
while (1) {
my $pid = $$;
my @children = ();
There is no need to explicitly clear a varaible declared using my().
@children will be empty already.
my @children;
Try to use the natural representation. The natural representation of
the concept of a scalar not being defined is the special scalar value
undef. As it hapens this is also whay my() will initialise scalars
to!
my $parent;
Actually for reasons I'll get to latter you don't need $parent at all.
@LIST = ("a", "b", "c", "d", "e",);
You forgot to delare @LIST.
You got no error because you "forgot" to use strict.
my @LIST = ("a", "b", "c", "d", "e",);
You forgot to delare $item.
You got no error because you "forgot" to use strict.
foreach my $item(@LIST) {
$rVar = "r$item";
$wVar = "w$item";
pipe ($rVar, $wVar);
You forgot to delare $rVar and $wVar.
You got no error because you "forgot" to use strict.
Are you beginning to see a pattern yet?
Since you use these variables outside the loop you'd need to decalre
them outside the loop, however I think it's better to declare them
within the loop and re-arrange the code.
$rVar and $wVar are symbolic references. You should never use symbolic
references where real references will do. If you always "use strict"
then you can tell Perl that you've decided you really need to use
symbolic references by saying "no strict 'refs'".
However there's no reason to use sumbolic refs here so don't.
pipe (my($rVar), my($wVar));
my $newPID = fork();
if (not defined $newPID) {
die "Fork didn't work\n";
You should usually include the error in your error message.
You should usually not suppress the line number from your error
message.
die "Fork didn't work: $!";
} elsif ( $newPID == 0 ) {
$parent = $pid;
$pid = $$;
@children = ();
@LIST = ();
You are clearing @LIST on the assumption that it will terminate the
for() loop. Don't do that. The documentation of foreach() stresses
the fact that it is not defined what happens if you change the value
of an array while you are interating over it. To exit a loop use
last().
However rather than exiting the loop, then having another if($parent)
to find out if you are in the child or the parent it's better to just
call the child code here then exit().
And I wouldn't bother clearing @children in the child. Memory is not
that expensive!
} else {
push @children, $newPID;
$childpipe{$newPID} = $item;
}
Rather than saving $item, you can save the handle $rVar. You don't
need to save $wVar because this is where you should be closing it
anyhow! Not that you actually need to do so explicitly - using
lexical filehandles takes care of that for you.
$childpipe{$newPID} = $rVar;
if ($parent) {
#child process..
close $rVar;
print $wVar $$;
exit (0);
This should go inside the loop - or (if it's likely to grow long)
inside a subroutine that's called from within the loop.
} else {
#parent process..
while (my $child = shift @children) {
my $justDied = waitpid ($child,0);
You are still relying on the fact that the child will die before the
parent has read the output from it. This is only going to be true if
the child's total output fits in a single pipe buffer. Anyhow, you
are not cheching the exist status so there was no point waiting at all.
$rVar = "r$childpipe{$child}";
$wVar = "w$childpipe{$child}";
close $wVar;
This close() is no longer needed, it was closed already!
$stuff = <$rVar>;
print "$childpipe{$child}, $stuff\n";
unless ($justDied == $child)
{
print "I wonder what died?\n";
}
}
close
Why are you closing STDOUT inside the loop? Once you've closed it all
further prints to it will fail.
Could this be your real problem? If so then your problem had nothing
to do with all your bad habits - but your bad habits made your code
much more difficult for us to understand your code.
Indeed I've alreay spent >30m helping you with obvious problems in
your code before I stumbled upon it.
And of course that spurious close() wasn't in the pseudo-code at all
so anybody looking at the pseudo-code was completely wasting their
time. I think you own an appology to Xho for calling him a cynic.
}
sleep 10;
}
Agreed, the problem is probably somewhere in my code - although I'll
be damned if I can find it!
Perhaps if you made your code simpler it would be easier?
Of course, simplifying you code can often fix the problem without you
ever needing to find it. Was is also what the so-called cynic was
trying to tell you.
#!/usr/bin/perl
use strict;
use warnings;
while (1) {
my $pid = $$;
my (@children,%childpipe);
my @LIST = ("a", "b", "c", "d", "e",);
foreach my $item (@LIST) {
pipe (my($rVar), my($wVar));
my $newPID = fork();
if (not defined $newPID) {
die "Fork didn't work: $!";
} elsif ( $newPID == 0 ) {
#child process..
undef $rVar; # close()
print $wVar $$;
exit (0);
} else {
push @children, $newPID;
$childpipe{$newPID} = $rVar;
}
}
#parent process..
while (my $child = shift @children) {
my $rVar = $childpipe{$child};
my $stuff = <$rVar>;
print "$child, $stuff\n";
}
sleep 10;
}
__END__
Note that for convenience you can also combine the pipe() and fork()
(and optionally exec() too) into open() thus:
my $newPID = open my $rVar, '-|';
Here you don't need the $rVar variable because STDOUT in the child
becomes connected to the pipe.
--
\\ ( )
. _\\__[oo
.__/ \\ /\@
. l___\\
# ll l\\
###LL LL\\