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

Reply via email to