Acme-Sort-Sleep
view release on metacpan or search on metacpan
local/lib/perl5/IO/Async/Process.pm view on Meta::CPAN
=head2 command => ARRAY or STRING
Either a reference to an array containing the command and its arguments, or a
plain string containing the command. This value is passed into perl's
C<exec(2)> function.
=head2 code => CODE
A block of code to execute in the child process. It will be called in scalar
context inside an C<eval> block.
=head2 setup => ARRAY
Optional reference to an array to pass to the underlying C<Loop>
C<spawn_child> method.
=head2 fdI<n> => HASH
A hash describing how to set up file descriptor I<n>. The hash may contain the
following keys:
=over 4
=item via => STRING
Configures how this file descriptor will be configured for the child process.
Must be given one of the following mode names:
=over 4
=item pipe_read
The child will be given the writing end of a C<pipe(2)>; the parent may read
from the other.
=item pipe_write
The child will be given the reading end of a C<pipe(2)>; the parent may write
to the other. Since an EOF condition of this kind of handle cannot reliably be
detected, C<on_finish> will not wait for this type of pipe to be closed.
=item pipe_rdwr
Only valid on the C<stdio> filehandle. The child will be given the reading end
of one C<pipe(2)> on STDIN and the writing end of another on STDOUT. A single
Stream object will be created in the parent configured for both filehandles.
=item socketpair
The child will be given one end of a C<socketpair(2)>; the parent will be
given the other. The family of this socket may be given by the extra key
called C<family>; defaulting to C<unix>. The socktype of this socket may be
given by the extra key called C<socktype>; defaulting to C<stream>. If the
type is not C<SOCK_STREAM> then a L<IO::Async::Socket> object will be
constructed for the parent side of the handle, rather than
L<IO::Async::Stream>.
=back
Once the filehandle is set up, the C<fd> method (or its shortcuts of C<stdin>,
C<stdout> or C<stderr>) may be used to access the
L<IO::Async::Handle>-subclassed object wrapped around it.
The value of this argument is implied by any of the following alternatives.
=item on_read => CODE
The child will be given the writing end of a pipe. The reading end will be
wrapped by an L<IO::Async::Stream> using this C<on_read> callback function.
=item into => SCALAR
The child will be given the writing end of a pipe. The referenced scalar will
be filled by data read from the child process. This data may not be available
until the pipe has been closed by the child.
=item from => STRING
The child will be given the reading end of a pipe. The string given by the
C<from> parameter will be written to the child. When all of the data has been
written the pipe will be closed.
=back
=head2 stdin => ...
=head2 stdout => ...
=head2 stderr => ...
Shortcuts for C<fd0>, C<fd1> and C<fd2> respectively.
=head2 stdio => ...
Special filehandle to affect STDIN and STDOUT at the same time. This
filehandle supports being configured for both reading and writing at the same
time.
=cut
sub configure
{
my $self = shift;
my %params = @_;
foreach (qw( on_finish on_exception )) {
$self->{$_} = delete $params{$_} if exists $params{$_};
}
# All these parameters can only be configured while the process isn't
# running
my %setup_params;
foreach (qw( code command setup stdin stdout stderr stdio ), grep { m/^fd\d+$/ } keys %params ) {
$setup_params{$_} = delete $params{$_} if exists $params{$_};
}
if( $self->is_running ) {
keys %setup_params and croak "Cannot configure a running Process with " . join ", ", keys %setup_params;
}
defined( exists $setup_params{code} ? $setup_params{code} : $self->{code} ) +
defined( exists $setup_params{command} ? $setup_params{command} : $self->{command} ) <= 1 or
croak "Cannot have both 'code' and 'command'";
foreach (qw( code command setup )) {
$self->{$_} = delete $setup_params{$_} if exists $setup_params{$_};
}
$self->configure_fd( 0, %{ delete $setup_params{stdin} } ) if $setup_params{stdin};
$self->configure_fd( 1, %{ delete $setup_params{stdout} } ) if $setup_params{stdout};
$self->configure_fd( 2, %{ delete $setup_params{stderr} } ) if $setup_params{stderr};
$self->configure_fd( 'io', %{ delete $setup_params{stdio} } ) if $setup_params{stdio};
# All the rest are fd\d+
foreach ( keys %setup_params ) {
my ( $fd ) = m/^fd(\d+)$/ or croak "Expected 'fd\\d+'";
$self->configure_fd( $fd, %{ $setup_params{$_} } );
}
$self->SUPER::configure( %params );
}
# These are from the perspective of the parent
use constant FD_VIA_PIPEREAD => 1;
use constant FD_VIA_PIPEWRITE => 2;
use constant FD_VIA_PIPERDWR => 3; # Only valid for stdio pseudo-fd
use constant FD_VIA_SOCKETPAIR => 4;
my %via_names = (
pipe_read => FD_VIA_PIPEREAD,
pipe_write => FD_VIA_PIPEWRITE,
pipe_rdwr => FD_VIA_PIPERDWR,
socketpair => FD_VIA_SOCKETPAIR,
);
sub configure_fd
{
my $self = shift;
my ( $fd, %args ) = @_;
$self->is_running and croak "Cannot configure fd $fd in a running Process";
if( $fd eq "io" ) {
exists $self->{fd_opts}{$_} and croak "Cannot configure stdio since fd$_ is already defined" for 0 .. 1;
}
elsif( $fd == 0 or $fd == 1 ) {
exists $self->{fd_opts}{io} and croak "Cannot configure fd$fd since stdio is already defined";
}
my $opts = $self->{fd_opts}{$fd} ||= {};
my $via = $opts->{via};
my ( $wants_read, $wants_write );
if( my $via_name = delete $args{via} ) {
defined $via and
croak "Cannot change the 'via' mode of fd$fd now that it is already configured";
$via = $via_names{$via_name} or
croak "Unrecognised 'via' name of '$via_name'";
}
if( my $on_read = delete $args{on_read} ) {
$opts->{handle}{on_read} = $on_read;
$wants_read++;
}
elsif( my $into = delete $args{into} ) {
$opts->{handle}{on_read} = sub {
my ( undef, $buffref, $eof ) = @_;
local/lib/perl5/IO/Async/Process.pm view on Meta::CPAN
my $self = shift;
return $self->{dollarbang}+0;
}
=head2 errstr
$errstr = $process->errstr
If the process exited due to an exception, returns the string value of
C<$!> at the time the exception was thrown. Otherwise, returns C<undef>.
=cut
sub errstr
{
my $self = shift;
return $self->{dollarbang}."";
}
=head2 fd
$stream = $process->fd( $fd )
Returns the L<IO::Async::Stream> or L<IO::Async::Socket> associated with the
given FD number. This must have been set up by a C<configure> argument prior
to adding the C<Process> object to the C<Loop>.
The returned object have its read or write handle set to the other end of a
pipe or socket connected to that FD number in the child process. Typically,
this will be used to call the C<write> method on, to write more data into the
child, or to set an C<on_read> handler to read data out of the child.
The C<on_closed> event for these streams must not be changed, or it will break
the close detection used by the C<Process> object and the C<on_finish> event
will not be invoked.
=cut
sub fd
{
my $self = shift;
my ( $fd ) = @_;
return $self->{fd_handle}{$fd} ||= do {
my $opts = $self->{fd_opts}{$fd} or
croak "$self does not have an fd Stream for $fd";
my $handle_class;
if( defined $opts->{socktype} && IO::Async::OS->getsocktypebyname( $opts->{socktype} ) != SOCK_STREAM ) {
require IO::Async::Socket;
$handle_class = "IO::Async::Socket";
}
else {
require IO::Async::Stream;
$handle_class = "IO::Async::Stream";
}
my $handle = $handle_class->new(
notifier_name => $fd eq "0" ? "stdin" :
$fd eq "1" ? "stdout" :
$fd eq "2" ? "stderr" :
$fd eq "io" ? "stdio" : "fd$fd",
%{ $opts->{handle} },
);
if( defined $opts->{from} ) {
$handle->write( $opts->{from},
on_flush => sub {
my ( $handle ) = @_;
$handle->close_write;
},
);
}
$handle
};
}
=head2 stdin
=head2 stdout
=head2 stderr
=head2 stdio
$stream = $process->stdin
$stream = $process->stdout
$stream = $process->stderr
$stream = $process->stdio
Shortcuts for calling C<fd> with 0, 1, 2 or C<io> respectively, to obtain the
L<IO::Async::Stream> representing the standard input, output, error, or
combined input/output streams of the child process.
=cut
sub stdin { shift->fd( 0 ) }
sub stdout { shift->fd( 1 ) }
sub stderr { shift->fd( 2 ) }
sub stdio { shift->fd( 'io' ) }
=head1 EXAMPLES
=head2 Capturing the STDOUT stream of a process
By configuring the C<stdout> filehandle of the process using the C<into> key,
data written by the process can be captured.
my $stdout;
my $process = IO::Async::Process->new(
command => [ "writing-program", "arguments" ],
stdout => { into => \$stdout },
on_finish => sub {
print "The process has finished, and wrote:\n";
print $stdout;
}
);
$loop->add( $process );
Note that until C<on_finish> is invoked, no guarantees are made about how much
of the data actually written by the process is yet in the C<$stdout> scalar.
See also the C<run_child> method of L<IO::Async::Loop>.
To handle data more interactively as it arrives, the C<on_read> key can
instead be used, to provide a callback function to invoke whenever more data
is available from the process.
my $process = IO::Async::Process->new(
command => [ "writing-program", "arguments" ],
stdout => {
on_read => sub {
my ( $stream, $buffref ) = @_;
while( $$buffref =~ s/^(.*)\n// ) {
print "The process wrote a line: $1\n";
}
return 0;
},
},
on_finish => sub {
print "The process has finished\n";
}
);
$loop->add( $process );
If the code to handle data read from the process isn't available yet when
the object is constructed, it can be supplied later by using the C<configure>
method on the C<stdout> filestream at some point before it gets added to the
Loop. In this case, C<stdin> should be configured using C<pipe_read> in the
C<via> key.
my $process = IO::Async::Process->new(
command => [ "writing-program", "arguments" ],
stdout => { via => "pipe_read" },
on_finish => sub {
print "The process has finished\n";
( run in 1.099 second using v1.01-cache-2.11-cpan-39bf76dae61 )