rovf has asked for the wisdom of the Perl Monks concerning the following question:

I stumbled over a bizarre effect in ActiveState Perl for Windows (in both the version 5.8.7 and 5.10.0), which can be shown as follows:

Consider I have a harmless program cat.pl, which just echoes its STDIN:

# This is cat.pl while(<>) { print; }
And then I have a marginally more harmful program ls.pl, which feeds some input to cat.pl and reads its output:
# This is ls.pl use strict; use warnings; use IPC::Open2; # $^X is the path to my Perl open2(*OUTPUT,*INPUT,"$^X", "cat.pl") or die "$!"; print INPUT "first line\nsecond line\n"; close INPUT; my @result=<OUTPUT>; close OUTPUT; print "RESULT:\n@result\n";
When I execute under Solaris or under Cygwin
perl ls.pl
I get as output
RESULT: first line second line
But when I do it under Windows, the program hangs, as if it would wait indefinitely for input.

I have no explanation for this odditiy. The only hint, that even under Solaris, respectively Cygwin, something unusual is going on, is the fact that in the (otherwise correct) output, the last line is preceded by a single space.

Any suggestion on what is going on here, and how I could get it running on Windows?
-- 
Ronald Fischer <ynnor@mm.st>

Replies are listed 'Best First'.
Re: ActiveState woes : Is it EOF-blind?
by tachyon-II (Chaplain) on May 06, 2008 at 00:24 UTC

    Presumably the difference relates to what happens when you close the writer. You might logically expect this would trigger the sending of EOF, and presumably it does on most OS but not on Win32. It works on Win32 if you formally send the EOF char (0x1a):

    C:\>type cat.pl $|++; print while <>; C:\>type ipc.pl use strict; use IPC::Open2; $|++; my $EOF = chr(0x1a); # inline use "\x1a" my $perl = "H:\\perl\\bin\\perl.exe"; my $cat = "C:\\cat.pl"; my $data = "first line\nsecond line"; my $pid = open2(my $rdr, my $wtr, $perl, $cat ); print "Sending:\n$data\n----\n"; print $wtr $data .$EOF; print "Going for read!\n"; print while <$rdr>; C:\>perl ipc.pl Sending: first line second line ---- Going for read! first line second line C:\>

    This will hang if you don't send the 0x1a regardless of whether you close $wtr or not.

      You might logically expect this would trigger the sending of EOF, and presumably it does on most OS but not on Win32.

      Actually, it does on Win32 as well. It is just that Perl on Win32 now has a bug such that $^F doesn't work as it is supposed to. You can use Win32API::File::SetHandleInformation() to work around this bug. The following code does what IPC::Open3 should be doing under the covers, but with two added lines of code to work around this Perl bug:

      #!/usr/bin/perl -w use strict; use Win32API::File qw( GetOsFHandle GetHandleInformation SetHandleInformation HANDLE_FLAG_INHERIT CloseHandle ); if( @ARGV ) { # This is cat.pl warn "Started child...\n"; for( @ARGV ) { if( /\D/ ) { } elsif( CloseHandle($_) ) { warn "Closed $_!\n"; } else { warn "Couldn't close $_: $^E\n"; } } while(<STDIN>) { warn "Child read line...\n"; print; warn "Child printed line...\n"; } warn "Child done...\n"; close STDOUT or die "Can't close STDOUT: $!\n"; close STDIN or die "Can't close STDIN: $!\n"; exit( 0 ); } pipe *KIDIN, *TOKID or die "Can't create first pipe: $!\n"; pipe *FROMKID, *KIDOUT or die "Can't create second pipe: $!\n"; open( STDIN, "<&", \*KIDIN ) or die "Can't dup2 KIDIN to STDIN: $!\n"; open( STDOUT, ">&", \*KIDOUT ) or die "Can't dup2 KIDOUT to STDOUT: $!\n"; close KIDIN or die "Can't close KIDIN: $!\n"; close KIDOUT or die "Can't close KIDOUT: $!\n"; # Here is the missing part: SetHandleInformation( GetOsFHandle(\*TOKID), HANDLE_FLAG_INHERIT(), 0 +) or die "Can't disable inherit for TOKID: $^E\n"; SetHandleInformation( GetOsFHandle(\*FROMKID), HANDLE_FLAG_INHERIT(), +0 ) or die "Can't disable inherit for FROMKID: $^E\n"; system( 1, $^X, $0, 'cat' ) # GetOsFHandle(\*TOKID), GetOsFHandle(\*FR +OMKID) ) or die "Can't exec self: $!\n"; # sleep 1; close STDIN or die "Can't close STDIN: $!\n"; close STDOUT or die "Can't close STDOUT: $!\n"; print TOKID "first line\nsecond line\n"; close TOKID or die "Can't close TOKID: $!\n"; warn "closed TOKID\n"; my @result= <FROMKID>; print STDERR "RESULT:\n", @result, "\n"; close FROMKID or die "Can't close FROMKID: $!\n";

      Using SetHandleInformation() to turn off the HANDLE_FLAG_INHERIT() bit causes those two file handles to not be inherited by the child (which is like closing the handles after fork but before exec).

      - tye        

        Thanks.

        If I understand correctly, the lines you added effectively do the close(TOKID) and close(FRKID) that would be done on the child side of the fork in a unix environment.

        Can you tell me, what the first arg of system ("1") in your example means? Can't find this in the docs....

      Sending chr(0x1a) has no effect for me. It doesn't work if I follow it with \n either. It gets successfully echoed back by cat and shows up as an arrow on the screen.

      Even though I didn't expect it to work, I had tried that earlier. Perl doesn't treat chr(0x1A) specially, only CRLF.

        By has no effect what do you mean? With the reader loop as written, on Win32, without the 0x1a it gets both lines, then hangs (blocks), awiating EOF. With the 0x1a it detects the end of the input and then finishes. Tested on Win2k SP2, AS perl 5.6.1

        To get it to run as written on Linux (CentOS) perl 5.10 you need to close $wtr. Sending 0x1a makes no difference.

        [james@adajio ~]$ cat cat.pl $|++; print while <>; [james@adajio ~]$ cat ipc.pl #!/usr/bin/perl use strict; use IPC::Open2; $|++; my $EOF = ''; # chr(0x1a); # inline use "\x1a" my $perl = "/usr/bin/perl"; my $cat = "/home/james/cat.pl"; my $data = "first line\nsecond line"; my $pid = open2(my $rdr, my $wtr, $perl, $cat ); print "Sending:\n$data\n----\n"; print $wtr $data .$EOF; close $wtr; print "Going for read!\n"; print while <$rdr>; [james@adajio ~]$ ./ipc.pl Sending: first line second line ---- Going for read! first line second line [james@adajio ~]$

        So on linux the act of closing $wtr effectively sends the EOF signal. Physically sending apparently does nothing. Sending a \n at the end of the $data string allows reading of the second line but it still hangs awaiting EOF. Thus a portable way of doing it is to send the EOF (makes it work on Win32) and close $wtr (makes it work on *nix) - as this ensures perl gets a detectable EOF into the $rdr stream.

      Thank you to all your suggestions. So I tried to modify my example program in two ways:
      • Make output unbuffered in both programs
      • Make sure that Control-Z is sent on Windows before the output handle is closed.
      This results in the following programs:
      # This is cat.pl $|=1; while(<>) { print; } chr(0x1a) if $^O eq 'MSWin32';
      and
      # This is ls.pl use strict; use warnings; use IPC::Open2; # OUTPUT: Filehandle for output from cat.pl # INPUT: Filehandle for input to cat.pl open2(*OUTPUT,*INPUT,"$^X", "cat.pl") or die "$!"; print INPUT "first line\nsecond line\n"; if ($^O eq 'MSWin32') { select INPUT; $| = 1; print INPUT chr(0x1a); } close INPUT; my @result=<OUTPUT>; close OUTPUT; print "RESULT:\n@result\n";
      Still, using the programs on Windows like this:
      perl ls.pl
      causes it to hang.
      -- 
      Ronald Fischer <ynnor@mm.st>

        You seem to have incorporated all the comments except the ones that mattered.

        • $|=1 for STDOUT in cat.pl is technically not needed in this case because you expect the child to end before you start reading. It's good to have, though.

        • $|=1 for INPUT in ls.pl is already done by open2. And if it wasn't, it would need to be done in unix too.

        • Your problem is caused by a bug in IPC::Open3 and by proxy in IPC::Open2.

        • The chr(0x1a) shouldn't be needed, but it's a workaround for the bug. However, it doesn't work with PerlIO (which is used by ActiveState since 5.8 and probably used by the default on all Perls since 5.8).

        By the way, the die "$!" in open2(*OUTPUT,*INPUT,"$^X", "cat.pl") or die "$!"; will never get executed since open2 throws an exception on error. Good thing, cause $! is only appropriate for systems calls and this isn't one.

Re: ActiveState woes : Is it EOF-blind?
by starbolin (Hermit) on May 05, 2008 at 19:43 UTC

    You actually have two problems in there. The first is Suffering from Buffering. The second is in trying to evoke the perl interpreter and expect it to pass your filehandles to it's child ( I tried to puzzle out what actually happens to STDIO during this but that just made my head hurt.) IPC::Open2 has a good suggestion:

    "The big problem with this approach is that if you don't have control over source code being run in the child process, you can't control what it does with pipe buffering. Thus you can't just open a pipe to "cat -v" and continually read and write a line from it." "The IO::Pty and Expect modules from CPAN can help with this, as they provide a real tty (well, a pseudo-tty, actually), which gets you back to line buffering in the invoked command again."


    s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
      The second problem you mention ("The second is in trying to evoke the perl interpreter and expect it to pass your filehandles to it's child") makes no sense to me: The launched perl doesn't have a child. Could you explain?

        ikegami writes:

        The launched perl doesn't have a child. Could you explain?
        Yes, I misspoke, must have been temporarily missing all clue. Had been working on calling the shell from qx() and was lost in an incorrect mind set. Of course what ikegami is talking about is that perl is interpreting the users cat.pl script and not forking a child . Further discussion here has clarified ( somewhat ) that the problem lies in another direction.


        s//----->\t/;$~="JAPH";s//\r<$~~/;{s|~$~-|-~$~|||s |-$~~|$~~-|||s,<$~~,<~$~,,s,~$~>,$~~>,, $|=1,select$,,$,,$,,1e-1;print;redo}
      "The IO::Pty and Expect modules from CPAN can help with this, ...

      Unfortunately, those modules don't work on native Windows...

Re: ActiveState woes : Is it EOF-blind?
by Corion (Patriarch) on May 05, 2008 at 16:55 UTC

    See perlipc, the section about Bidirectional Communication with Another Process. I cite: The problem with this is that Unix buffering is really going to ruin your day. This applies not only to Unix but also to Win32, and you're running up against different buffer sizes here.

      I can't get it to work, even if I turn off buffering on both programs.
Re: ActiveState woes : Is it EOF-blind?
by ig (Vicar) on May 06, 2008 at 07:10 UTC
    The only hint, that even under Solaris, respectively Cygwin, something unusual is going on, is the fact that in the (otherwise correct) output, the last line is preceded by a single space.

    The space is probably the list separator character ($") which separates the list elements when your array is interpolated into the double quoted string.

    See LIST_SEPARATOR in perlvar for details

    perldoc perlvar
Re: ActiveState woes : Is it EOF-blind?
by girarde (Hermit) on May 08, 2008 at 17:02 UTC
    I've never used IPC::Open2, but I would expect it has Unixy roots that lead to failure on Win32. Windows just doesn't do what Unix, and therefore most of Perl, expects here.

    In short, whatever task you're trying to accomplish on Windows with IPC::Open2 probably requires a different approach.