Today, I came across a puzzling (to me) behavior of wait and
IO::Handle. Although I managed to work around it, I'd love to
understand what's happening.
Here is a very simple IPC program: parent spawns children, reads their
output, and collects their exit codes. This code doesn't illustrate
the puzzling behavior. Rather, it illustrates the way I'd expect
things to work.
------------------------------------------------------------------
#!/usr/bin/env perl -w
use strict;
$| = 1;
sub main() {
my @kids = ();
for (1..10) {
my $fh = anon_fh();
my $pid = open($fh, "-|");
if ($pid == 0) {
kid_stuff();
}
else {
push(@kids, [ $pid, $fh ]);
}
}
foreach my $kid (@kids) {
my ($pid, $fh) = @$kid;
my $wpid = waitpid($pid, 0);
chomp(my $output = <$fh>);
print("DONE: PID=$wpid, status=$? output=$output\n");
close($fh);
}
}
sub kid_stuff() {
exec("echo kid");
}
sub anon_fh() {
local *FH;
return *FH;
}
main();
------------------------------------------------------------------
And here is the output, showing the child pids, exit codes, and
child process output.
DONE: PID=491, status=0 output=kid
DONE: PID=492, status=0 output=kid
DONE: PID=493, status=0 output=kid
DONE: PID=494, status=0 output=kid
DONE: PID=495, status=0 output=kid
DONE: PID=496, status=0 output=kid
DONE: PID=497, status=0 output=kid
DONE: PID=498, status=0 output=kid
DONE: PID=499, status=0 output=kid
DONE: PID=500, status=0 output=kid
So far so good; now for the puzzling part. Below is a variation that
uses IO::Handle instead of glob references. I've marked the changed
lines with "# new".
------------------------------------------------------------------
#!/usr/bin/env perl -w
use strict;
use IO::Handle; # new
$| = 1;
sub main() {
my @kids = ();
for (1..10) {
my $fh = anon_fh();
my $pid = open($fh, "-|");
if ($pid == 0) {
kid_stuff();
}
else {
my $handle = IO::Handle->new(); # new
$handle->fdopen(fileno($fh), "r"); # new
push(@kids, [ $pid, $handle ]);
}
}
foreach my $kid (@kids) {
my ($pid, $fh) = @$kid;
my $wpid = waitpid($pid, 0);
chomp(my $output = $fh->getline()); # new
print("DONE: PID=$wpid, status=$? output=$output\n");
$fh->close(); # new
}
}
sub kid_stuff() {
exec("echo kid");
}
sub anon_fh() {
local *FH;
return *FH;
}
main();
------------------------------------------------------------------
I'd expect the output of program #2 to be similar to the output of
program #1. But instead, I get this:
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
DONE: PID=-1, status=-1 output=kid
Clearly, the parent was able to read the child process's stdout; but,
we effectively lost $? and the return value of waitpid.
Perl 5.8.6 on Mac OS X 10.4.11, perl 5.8.8 on Mac OS X 10.5.4, and
perl 5.8.5 on Centos 4.4 all behave the same way. Whatever's
happening, at least it seems to be consistent.
Do any of you know why IO::Handle (appears to) break wait?
Steve
_______________________________________________
Boston-pm mailing list
[email protected]
http://mail.pm.org/mailman/listinfo/boston-pm