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. | [reply] [d/l] |
|
|
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] |
|
|
|
|
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] |
|
|
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] |
|
|
|
|
|
|
|
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] |
|
|
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}
| [reply] |
|
|
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?
| [reply] |
|
|
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}
| [reply] [d/l] |
|
|
| [reply] |
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.
| [reply] |
|
|
I can't get it to work, even if I turn off buffering on both programs.
| [reply] |
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
| [reply] [d/l] |
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. | [reply] [d/l] [select] |