--- AnyEvent-Fork-Remote/Remote.pm 2013/04/27 01:44:55 1.1 +++ AnyEvent-Fork-Remote/Remote.pm 2013/04/27 23:59:04 1.2 @@ -57,6 +57,7 @@ use common::sense; +use Carp (); use Errno (); use AnyEvent (); @@ -140,8 +141,36 @@ $done->($a); }; +=item my $proc = new_from_fh $fh + +Creates an C object from a file handle. This file +handle must be connected to both STDIN and STDOUT of a F process. + +This form might be more convenient than C or C when +creating an C object, but the resulting object +does not support C. + =cut +sub new { + my ($class, $create) = @_; + + bless [ + $create, + "", + [], + ], $class +} + +sub new_from_fh { + my ($class, @fh) = @_; + + $class->new (sub { + shift @fh + or Carp::croak "AnyEvent::Fork::Remote::new_from_fh does not support fork"; + }); +} + sub new_exec { my ($class, $program, @argv) = @_; @@ -169,16 +198,6 @@ }) } -sub new { - my ($class, $create) = @_; - - bless [ - $create, - "", - [], - ], $class -} - =item $new_proc = $proc->fork Quite the same as the same method of L, except that it @@ -214,8 +233,7 @@ =cut sub send_fh { - require Carp; - Carp::croak ("send_fh is not supported on AnyEvent::Fork::Remote objects"); + Carp::croak "send_fh is not supported on AnyEvent::Fork::Remote objects"; } =item $proc = $proc->eval ($perlcode, @args) @@ -243,7 +261,12 @@ sub eval { my ($self, $perlcode, @args) = @_; - $self->[1] .= '{ local @_ = ' . (aq @args) . "; $perlcode }\n"; + my $linecode = $perlcode; + $linecode =~ s/\s+/ /g; # takes care of \n + $linecode =~ s/"/''/g; + substr $linecode, 70, length $linecode, "..." if length $linecode > 70; + + $self->[1] .= '{ local @_ = ' . (aq @args) . ";\n#line 1 \"'$linecode'\"\n$perlcode;\n}\n"; } =item $proc = $proc->require ($module, ...) @@ -255,8 +278,8 @@ sub require { my ($self, @modules) = @_; - s%::%/%g for @modules; - $self->eval ('require "$_.pm" for @_', @modules); + $self->eval ("require $_") + for @modules; $self } @@ -279,18 +302,24 @@ Very similar to the run method of L. -On the parent side, the API is identical. On the child side, the -"communications socket" is in fact just C<*STDIN>, and typically can only -be read from. - -If the run function wants to read data that is written to C<$fh> in the -parent, then it should read from STDIN. If the run function wants to -provide data that can later be read from C<$fh>, then it should write them -to STDOUT. - -You can write a run function that works with both L and -this module by checking C in on the passed callback in the run -function: +On the parent side, the API is identical, except that a C<$cb> argument of +C instad of a valid file handle signals an error. + +On the child side, the "communications socket" is in fact just C<*STDIN>, +and typically can only be read from (this highly depends on how the +program is created - if you just run F locally, it will work for +both reading and writing, but commands such as F or F typically +only provide read-only handles for STDIN). + +To be portable, if the run function wants to read data that is written to +C<$fh> in the parent, then it should read from STDIN. If the run function +wants to provide data that can later be read from C<$fh>, then it should +write them to STDOUT. + +You can write a run function that works with both L +and this module by checking C. If it is C<0> (meaning +it is STDIN), then you should use it for reading, and STDOUT for +writing. Otherwise, you should use the file handle for both: sub run { my ($rfh, ...) = @_; @@ -304,39 +333,63 @@ sub run { my ($self, $func, $cb) = @_; - my $code = 'BEGIN {' . $self->[1] . '}' - . 'syswrite STDOUT, ' . (sq $magic0) . '^' . (sq $magic1) . ';' - . $func . (aq @{ $self->[2] }) . ';' - . "\n__END__\n"; - $self->[0](sub { my $fh = shift or die "AnyEvent::Fork::Remote: create callback failed"; - - + my $code = 'BEGIN {' . $self->[1] . "}\n" + . 'syswrite STDOUT, ' . (sq $magic0) . '^' . (sq $magic1) . ';' + . '{ sysread STDIN, my $dummy, 1 }' + . "\n$func*STDIN," . (aq @{ $self->[2] }) . ';' + . "\n__END__\n"; + + warn $code;#d# + + AnyEvent::Util::fh_nonblocking $fh, 1; + + my ($rw, $ww); + + my $ofs; + + $ww = AE::io $fh, 1, sub { + my $len = syswrite $fh, $code, 1<<20, $ofs; + + if ($len || $! == Errno::EAGAIN || $! == Errno::EWOULDBLOCK) { + $ofs += $len; + undef $ww if $ofs >= length $code; + } else { + # error + ($ww, $rw) = (); $cb->(undef); + } + }; + + my $rbuf; + + $rw = AE::io $fh, 0, sub { + my $len = sysread $fh, $rbuf, 1<<10; + + if ($len || $! == Errno::EAGAIN || $! == Errno::EWOULDBLOCK) { + $rbuf = substr $rbuf, -length $magic0 if length $rbuf > length $magic0; + + if ($rbuf eq ($magic0 ^ $magic1)) { + # all data was sent, magic was received - both + # directions should be "empty", and therefore + # the socket must accept at least a single octet, + # to signal the "child" to go on. + undef $rw; + die if $ww; # uh-oh + + syswrite $fh, "\n"; + $cb->($fh); + } + } else { + # error + ($ww, $rw) = (); $cb->(undef); + } + }; }); } -my $x = new_exec AnyEvent::Fork::Remote "/usr/bin/rsh", "rsh", "rain", "exec perl";#d# -$x->require ("Carp", "Storable");#d# -$x->send_arg (1, 2, 3);#d# -$x->eval ('sub run { die }');#d# -$x->run ("run", sub { - }); - - -=item my $proc = new_from_stdio $fh - -Creates an C object from a file handle. This file -handle must be connected to both STDIN and STDOUT of a F process. - -This form might be more convenient than C or C when -creating an C object, but the resulting object -does not support C. - -#TODO: really implement? - =back =head1 SEE ALSO