in reply to ActiveState woes : Is it EOF-blind?

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.

Replies are listed 'Best First'.
Re^2: ActiveState woes : Is it EOF-blind? (perl bug)
by tye (Sage) on May 06, 2008 at 05:21 UTC
    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....
Re^2: ActiveState woes : Is it EOF-blind?
by BrowserUk (Patriarch) on May 06, 2008 at 02:00 UTC
Re^2: ActiveState woes : Is it EOF-blind?
by ikegami (Patriarch) on May 06, 2008 at 02:14 UTC

    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.

        By has no effect what do you mean?

        I mean it doesn't change the outcome. The program behaves as if it wasn't there. It hangs.

        Tested on Win2k SP2, AS perl 5.6.1

        Well that's different. Various ActiveState builds on WinXP:
        VersionBuildResult w/o EOFResult w/ EOF
        5.6.0Active State build 623HangsProgram ends as desired
        5.6.1Active State build 635HangsProgram ends as desired
        5.8.0Active State build 806HangsHangs
        5.8.8Active State build 817HangsHangs
        5.10.0Active State build 1001HangsHangs

        I suspect you'd get the same results on Win2k.

        This is why:

        >debug
        -e100 "abc",0D,0A,1A,0D,0A,"def",0D,0A
        -rcx
        CX 0000
        :0D
        -ntest
        -w
        Writing 0000D bytes
        -q
        
        >c:\progs\perl560\bin\perl -ple1 test
        abc
        
        >c:\progs\perl561\bin\perl -ple1 test
        abc
        
        >c:\progs\perl580\bin\perl -ple1 test
        abc
        →
        def
        
        >c:\progs\perl588\bin\perl -ple1 test
        abc
        →
        def
        
        >c:\progs\perl5100\bin\perl -ple1 test
        abc
        →
        def
        

        More specifically, it's probably related to the introduction of PerlIO.

        >c:\progs\perl560\bin\perl -V | find /i "perlio"
            useperlio=undef d_sfio=undef uselargefiles=undef
        
        >c:\progs\perl561\bin\perl -V | find /i "perlio"
            useperlio=undef d_sfio=undef uselargefiles=undef usesocks=undef
        
        >c:\progs\perl580\bin\perl -V | find /i "perlio"
            useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
            cc='cl', ccflags ='-nologo -Gf -W3 -MD -Zi -DNDEBUG -O1 -DWIN32 -D_CONSOLE -
        DNO_STRICT -DHAVE_DES_FCRYPT  -DPERL_IMPLICIT_CONTEXT -DPERL_IMPLICIT_SYS -DUSE_
        PERLIO -DPERL_MSVCRT_READFIX',
        
        >c:\progs\perl588\bin\perl -V | find /i "perlio"
            useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
            cc='cl', ccflags ='-nologo -GF -W3 -MD -Zi -DNDEBUG -O1 -DWIN32 -D_CONSOLE -
        DNO_STRICT -DHAVE_DES_FCRYPT -DNO_HASH_SEED -DUSE_SITECUSTOMIZE -DPERL_IMPLICIT_
        CONTEXT -DPERL_IMPLICIT_SYS -DUSE_PERLIO -DPERL_MSVCRT_READFIX',
                                USE_PERLIO USE_SITECUSTOMIZE
        
        >c:\progs\perl5100\bin\perl -V | find /i "perlio"
            useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
            cc='cl', ccflags ='-nologo -GF -W3 -MD -Zi -DNDEBUG -O1 -DWIN32 -D_CONSOLE -
        DNO_STRICT -DHAVE_DES_FCRYPT -DUSE_SITECUSTOMIZE -DPRIVLIB_LAST_IN_INC -DPERL_IM
        PLICIT_CONTEXT -DPERL_IMPLICIT_SYS -DUSE_PERLIO -DPERL_MSVCRT_READFIX',
                                USE_LARGE_FILES USE_PERLIO USE_SITECUSTOMIZE
        

        Perl shouldn't have to treat \x1A specially (since it's only needed when reading from the console which is handled by the system) and it *must not* treat \x1A specially in some cases (such as when the encoding is UTF-16 or when the input can be "binary"), so I believe PerlIO is *correct* and there's actually a bug somewhere and this whole \x1A thing is a workaround that no longer works.

Re^2: ActiveState woes : Is it EOF-blind?
by rovf (Priest) on May 06, 2008 at 08:47 UTC
    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.

        Point taken! :-/
        Thank your for your helpful explanation!
        -- 
        Ronald Fischer <ynnor@mm.st>