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).
| [reply] [d/l] |
|
|
| [reply] |
|
|
Can you tell me, what the first arg of system ("1") in your example means?
Can't find this in the docs....
| [reply] |
|
|
| [reply] |
Re^2: ActiveState woes : Is it EOF-blind?
by BrowserUk (Patriarch) on May 06, 2008 at 02:00 UTC
|
Good point++.
As an addendum, if you binmode the output filehandle, you probably won't have to send ^Z, as that's a text-mode convention like line-ending translation. (Untested!)
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] |
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.
| [reply] [d/l] |
|
|
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. | [reply] [d/l] |
|
|
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:
| Version | Build | Result w/o EOF | Result w/ EOF |
| 5.6.0 | Active State build 623 | Hangs | Program ends as desired |
| 5.6.1 | Active State build 635 | Hangs | Program ends as desired |
| 5.8.0 | Active State build 806 | Hangs | Hangs |
| 5.8.8 | Active State build 817 | Hangs | Hangs |
| 5.10.0 | Active State build 1001 | Hangs | Hangs |
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.
| [reply] |
|
|
|
|
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>
| [reply] [d/l] [select] |
|
|
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.
| [reply] [d/l] [select] |
|
|
| [reply] |