Scary IPC::Open3 filehandle difference

M

Mike Hunter

Hi,

I was experimenting with Open3 and wrote the following two programs:

======buggy.pl:
#!/usr/local/bin/perl -w

use strict;

foreach my $i (1 .. 100)
{
print STDERR "STDERR $i\n";
print "STDOUT $i\n";
}

======buggy.pl:
#!/usr/local/bin/perl -w

use strict;
use IPC::Open3;
use IO::Select;

print "starting wrapper...\n";

my $pid = open3(\*WTRFH, \*RDRFH, \*ERRFH, @ARGV);

my $s = IO::Select->new();

#$s->add($wtrfh);
$s->add(\*RDRFH);
$s->add(\*ERRFH);

my $timeout = 1;
my $first_timeout = 3600;
my @ready = $s->can_read($first_timeout);

print "scalar @ready = ".(scalar @ready)."\n";

my $timeouts = 0;
while (scalar @ready)
{
print "on timeout $timeouts\n";
$timeouts++;
my $i = 0;
foreach my $fh (@ready)
{
my $result;

my $j = 0;
while ($result = <$fh>)
{
print "read $j: fh_$i #$fh# says: #".$result."#\n";
$j++;
}
$i++;
}
@ready = $s->can_read($timeout);
print "scalar @ready = ".(scalar @ready)."\n";
}
print "wrapper finished\n";

Ok, so I run the following:

../wrapper.pl ./buggy.pl

and I get

starting wrapper...
scalar GLOB(0x805cba4) GLOB(0x805cb44) = 2
on timeout 0
read 0: fh_0 #GLOB(0x805cba4)# says: #STDOUT 1
#
read 1: fh_0 #GLOB(0x805cba4)# says: #STDOUT 2
#
read 2: fh_0 #GLOB(0x805cba4)# says: #STDOUT 3
#
read 3: fh_0 #GLOB(0x805cba4)# says: #STDOUT 4
#
read 4: fh_0 #GLOB(0x805cba4)# says: #STDOUT 5
#
read 5: fh_0 #GLOB(0x805cba4)# says: #STDOUT 6
#
read 6: fh_0 #GLOB(0x805cba4)# says: #STDOUT 7
#
read 7: fh_0 #GLOB(0x805cba4)# says: #STDOUT 8
#
read 8: fh_0 #GLOB(0x805cba4)# says: #STDOUT 9
#
read 9: fh_0 #GLOB(0x805cba4)# says: #STDOUT 10
#
read 0: fh_1 #GLOB(0x805cb44)# says: #STDERR 1
#
read 1: fh_1 #GLOB(0x805cb44)# says: #STDERR 2
#
read 2: fh_1 #GLOB(0x805cb44)# says: #STDERR 3
#
read 3: fh_1 #GLOB(0x805cb44)# says: #STDERR 4
#
read 4: fh_1 #GLOB(0x805cb44)# says: #STDERR 5
#
read 5: fh_1 #GLOB(0x805cb44)# says: #STDERR 6
#
read 6: fh_1 #GLOB(0x805cb44)# says: #STDERR 7
#
read 7: fh_1 #GLOB(0x805cb44)# says: #STDERR 8
#
read 8: fh_1 #GLOB(0x805cb44)# says: #STDERR 9
#
read 9: fh_1 #GLOB(0x805cb44)# says: #STDERR 10
#
....

(still need to write the code to close-out properly)

Anyway, my frist draft had wrapper.pl with the following slightly-different
opening lines:

my ($wtrfh, $rdrfh, $errfh);
my $pid = open3($wtrfh, $rdrfh, $errfh, @ARGV);

my $s = IO::Select->new();

#$s->add($wtrfh);
$s->add($rdrfh);
$s->add($errfh);

And I get the following nutz-oid output:

starting wrapper...
scalar GLOB(0x805cb2c) = 1
on timeout 0
read 0: fh_0 #GLOB(0x805cb2c)# says: #STDERR 1
#
read 1: fh_0 #GLOB(0x805cb2c)# says: #STDERR 2
#
read 2: fh_0 #GLOB(0x805cb2c)# says: #STDERR 3
#
read 3: fh_0 #GLOB(0x805cb2c)# says: #STDERR 4
#
read 4: fh_0 #GLOB(0x805cb2c)# says: #STDERR 5
#
read 5: fh_0 #GLOB(0x805cb2c)# says: #STDERR 6
#
read 6: fh_0 #GLOB(0x805cb2c)# says: #STDERR 7
#
read 7: fh_0 #GLOB(0x805cb2c)# says: #STDERR 8
#
read 8: fh_0 #GLOB(0x805cb2c)# says: #STDERR 9
#
read 9: fh_0 #GLOB(0x805cb2c)# says: #STDERR 10
#
read 10: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 1
#
read 11: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 2
#
read 12: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 3
#
read 13: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 4
#
read 14: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 5
#
read 15: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 6
#
read 16: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 7
#
read 17: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 8
#
read 18: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 9
#
read 19: fh_0 #GLOB(0x805cb2c)# says: #STDOUT 10
#

So somehow, it does 20 reads on the same filehandle, which has magically gotten
STDOUT and STDERR of buggy.pl....

???

Oh perl gods, reveal unto me my wickedness.

Mike

BTW this is perl 5.8.2 on fbsd 5.3-beta5
 
I

Ilmari Karonen

my $pid = open3(\*WTRFH, \*RDRFH, \*ERRFH, @ARGV); ....
Anyway, my frist draft had wrapper.pl with the following slightly-different
opening lines:

my ($wtrfh, $rdrfh, $errfh);
my $pid = open3($wtrfh, $rdrfh, $errfh, @ARGV); ....
So somehow, it does 20 reads on the same filehandle, which has magically gotten
STDOUT and STDERR of buggy.pl....

The documentation for IPC::Open3 says:

If ERRFH is false, or the same file descriptor as RDRFH, then
STDOUT and STDERR of the child are on the same filehandle.

Note the part about ERRFH being false. Also note that undef is false.

So yes, according to the documentation it _is_ supposed to work like
that. That said, it does feel rather stupid. It also means the two
code examples in the synopsis don't do the same thing, even though the
latter certainly looks like it should do the same as the former.

So I suppose the question is how much legacy code would break if it
was changed to work in a more sensible way. Any guesses?
 

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,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top