Perl5 -> Perl 6: prcoesses

Running a single process

Running a single process in Perl 6 is as simple as invoking the run method. run is a method that builds up a new Proc object that is responsible to encapsulate a running process. In short, it is possible to state that run is the same as backticks to Perl 5, with the very exception that they return not the command output but a command handler (the Proc object). In other words, to run a process in Perl 6 you need only:
my $command_to_execute = 'ps';
my $arguments = '-aef';
my $proc = run $command_to_execute, $arguments;
say 'Exit code ' ~ $proc.exitcode ~ ' exited with signal ' ~ $proc.signal;
say 'Command was ' ~ $proc.command;
$proc.out.say;

The process is immediatly started and executed, so the command $command_to_execute is launched with the $arguments parameters. Its output is printed to screen and captured into the out method. The exitcode method provides the exit code of the command, and in the case the command has been killed via a signal, the signal provides the number of the signal (zero for a normal completion). The command method provides an array of what was effectively run, in other words, the command line launched.

Where is new? Use run, spawn and shell!

Why don’t run a Proc via its new constructor? First of all, the new constructor accepts only named parameters, and if you take a look at the documentation for Proc.new you will see there’s no way of specifying the command to run at all. The command (and its arguments) are specified via run or spawn or shell, and these are the only entry points to run a process. In other words:
  • if you don’t have a Proc instance, use run;
  • if you have a Proc instance, use either spwan or shell.
For instance, using spawn:
my $proc = Proc.new;
$proc.spawn( $command_to_execute, $arguments );
The difference between spawn and shell is that the latter does not allow you to specify the arguments separately and runs the comamnd with the shell:
my $proc = Proc.new;
$proc.shell( $command_to_execute ~ ' ' ~ $arguments );

Piping commands

Piping commands in Perl 6 is easier than in Perl 5, and you don’t need to open file handles with the pipe symbol, but to simply connect two processes:
my $proc = run $command_to_execute, $arguments, :out;
my $grep = run 'grep', 'perl', :in( $proc.out );
The above code runs the first $proc command and passes it to grep. The :out and :in allows the connections between the processes.

Asynchronous Processes

Background: Promise and Supply

Before going into details about how to use async processes, let’s introduce a couple of classes used to deal with async IPC: [Promise](https://docs.perl6.org/type/Promise) and [Supply](https://docs.perl6.org/type/Supply). The Supply is a thread-safe implementation of the Observer pattern, in other words Supply does implement a thread-safe container for the input/output notifications. The idea is that Supply implements a channel to which other instances can subscribe to receive the data stream. Suppliers can be of two main types:
  • live they provide data only since you connect to them;
  • on demand they provide the data from the beginning every time a new consumer attaches to it.
It is possible to create a Supply instance using the tap or act methods, to which you pass a block of code that will be executed once new data is available on the channel. The only difference between tap and act is that the latter executes the block of code within a single thread at a time, so it is a little more safe with respect to shared resources and critical sections. A Supply can return also a Promise, a Promise is an instance of something that will become completed in the future, in other words a Promise is an handler for a computation that is still going on and will complete in the next future. A Supply provides new data to the subscribers via the emit method. It is important to note that a Supply is not instantiated via the new method, rather it is obtained by a Supplier object. That said, the following piece of code provides a way to generate and consume events:
my $producer   = Supplier.new;
my $consumer_1 = $producer.Supply;

$consumer_1.tap( -> $event { say "Consumer 1: $event" } );
$producer.emit( 'HELLO WORLD!' );

my $consumer_2 = $producer.Supply;
$consumer_2.tap( -> $ev { say "Consumer 2: $ev" } );

$producer.emit( "Hello World $_!" ) for 1..3;
that produces the following output:
Consumer 1: HELLO WORLD!
Consumer 1: Hello World 1!
Consumer 2: Hello World 1!
Consumer 1: Hello World 2!
Consumer 2: Hello World 2!
Consumer 1: Hello World 3!
Consumer 2: Hello World 3!
As you can see, the $consumer_2 is attached only to the second part of the stream, and therefore the very first HELLO WOROLD! is printed out by only the first consumer. It is now time to get back to Promisess: a Promise is an handler for a computation that will end in the next future. A Promise can be in one of the following states:
  • Planned if it is executing (or has been scheduled for execution);
  • Kept in case of succesful completion;
  • Broken in case of problematic completion.
A Promise can be created via the new method or via a few factory methods:
  • start creates a Promise for the specified block of code with; the Promise will be kept on success of will be broken if an execption is thrown;
  • in creates a Promise that will succeed (i.e., will be kept) after a specified number of seconds. This is usually used to create a timer or timeout promise;
  • at similar to in, will provide a Promise that is kept at the specified time instant;
  • allof provides a Promise that succeeds (i.e., it’s kept) once all its inner promises have finished (either with success or abort);
  • anyof provides a succesful Promise as soon as one of the inner promises has completed (wither succesfully or not).
A Promise, being asynchronously, could end after the end of the of the main program loop, therefore it is possible to force the caller to wait for ongoing computations. The await method is used to wait for a single Promise or an array of Promisess. Once the promise is kept, the code provided by the then method is executed. That being said, the following is a simple snippet of code that creates a computation that is performed 10 seconds after its creation:
say "Creating the promise: " ~ now;
my $do_lately = Promise.in( 10 );
$do_lately.then( { say "Promise elapsed! " ~ now } );
say "Promise launched!";
await $do_lately;
that produces:
Creating the promise: Instant:1513001393.523191
Promise launched!
Promise elapsed! Instant:1513001403.528402
Let’s provide a slightly more complex example with multiple Promises scheduled at different times:
say 'Creating the promises: ' ~ now;
my @promises;
for ( 1 .. 3 ) -> $_ {
     if ( $_ % 2 == 0 ) {
         my $p = Promise.in( $_ * 2 );
         $p.then( { say "Promise $_ completed! " ~ now } );
         @promises.push: $p;
     }
    else {
        my $p = Promise.at( now + $_ + 5 );
        $p.then( { say "Promise $_ completed! " ~ now } );
        @promises.push: $p;
     }
}

say 'End of Promises setup ' ~ now;
await @promises;
for @promises -> $p {
    say "OK ? $p.result() STATUS: $p.status()";
}
The result method returns True if the promise has completed with success, while the status method returns Kept or another promise status like Broken or Planned. Please note that calling result will wait on the Promise. It is important to note that a Promise cannot be Kept or Broken once it has been Planned, in order to do such you need to pass thru a Vow object obtained via the vow method:
my $p = Promise.new;
my $vow = $p.vow;
$vow.break: 'Cancelled!';

@promises.push: $p;
that produces the following output:
An operation first awaited:
  in block <unit> at proc.p6 line 52

Died with the exception:
    Cancelled!
      in block <unit> at proc.p6 line 52

A Promise provides a Supply method to obtain the streams to which connect the streams of data.

Putting it all together and running async processes

Having introduced Supply and Promise it is now possible to have a look at Proc::Async that holds an async process. Such class provides an interface similar (but separated) to the Proc one: it is possible to use the Supply for standard output and error, and thru Promise allows a manager control flow to instrument the execution flow.
my $do_later = Proc::Async.new( 'host', 'www.perl6.org' );
$do_later.stdout.tap( -> $event { "[STDOUT] $event".say } );
$do_later.stderr.tap(  -> $event { "[STDERR] $event".say } );


say "Launching ... ";
my $timeout = Promise.in( 10 );
await Promise.anyof( $timeout, $do_later.start );
say "Completed!";

The above code creates a $do_later process handler for resolving a host name. Another Promise is used as a ten second timeout, and then the master loop waits for the first one that completes via Promise.anyof. This simply means that either the host name completes or the timeout expires. Before that, the stdout and stderr streams of type Supply are attached to a very simple handler. The above piece of code provides the following output:
Launching ...
[STDOUT] www.perl6.org has address 213.95.82.53

Completed!

The react block

The above tapping and awaiting piece of code can be condensed within a so called react block, that is a kind og given/when designed for async operations. The above piece of code becomes like the following:
my $do_later = Proc::Async.new( 'host', 'www.perl6.org' );
say "Launching ... ";

react {
    whenever $do_later.stdout {
        "[STDOUT] $_".say;
    }

    whenever $do_later.stderr {
        "[STDERR] $_".say;
    }

    whenever $do_later.start {
        say "FINISHED! " ~ $_.exitcode;
        done; # completes the react block
    }

    whenever Promise.in( 10 ) {
        "[TIMEOUT] expired!".say;
        done; #complete the react block
    }
}

say "Completed!";
The react block provides automagic tapping other Supply instances as well as automagic awaiting other Promise instances, resulting in a much more declarative piece of code. More in detail a react block runs all the whenever clauses and then awaits for all of them to perform or for any of them to call done. Therefore when a whenever is performed against a Supply it does tap such Supply, when it is performed other a Promise it does await on it.

The article Perl5 -> Perl 6: processes has been posted by Luca Ferrari on December 10, 2017