Mojo::IOLoop::HoboProcess - Subprocesses with MCE::Hobo
This document describes Mojo::IOLoop::HoboProcess version 0.002.
use feature 'say'; use Mojo::IOLoop::HoboProcess; # Operation that would block the event loop for 5 seconds my $subprocess = Mojo::IOLoop::HoboProcess->new; $subprocess->run( sub { my $subprocess = shift; sleep 5; return '♥', 'Mojolicious'; }, sub { my ($subprocess, $err, @results) = @_; say "Subprocess error: $err" and return if $err; say "I $results[0] $results[1]!"; } ); # Start event loop if necessary Mojo::IOLoop->start unless Mojo::IOLoop->is_running;
Like Mojo::IOLoop::Subprocess, spawns subprocesses with MCE::Hobo instead.
Mojo::IOLoop::HoboProcess allows Mojo::IOLoop to perform computationally expensive operations in subprocesses, without blocking the event loop.
This module is supported on all platforms where MCE::Shared is supported.
Mojo::IOLoop::HoboProcess implements the following attribute.
my $timeout = $subprocess->timeout; $subprocess->timeout(300);
Set to a non-zero value to enable timeout. The default is 0.
my $loop = $subprocess->ioloop; $subprocess = $subprocess->ioloop(Mojo::IOLoop->new);
Event loop object to control, defaults to the global Mojo::IOLoop singleton.
Mojo::IOLoop::HoboProcess inherits all methods from Mojo::Base and implements the following new ones.
sub { $^O eq 'MSWin32' ? shift->exit(0) : exit(0) }, # not recommended sub { shift->exit(0) }, # do this instead or call MCE::Hobo->exit(0) sub { exit(0) }, # safe on Cygwin and UNIX platforms
Exit a thread (Windows) or process (other platforms).
my $pid = $subprocess->pid; $$.$tid Windows (only), includes thread id $$ Cygwin and UNIX platforms
Process id of the spawned subprocess if available.
$subprocess = $subprocess->run( sub {...}, sub {...} );
Execute the first callback in a child process and wait for it to return one or more values, without blocking "ioloop" in the parent process. Then execute the second callback in the parent process with the results. The return values of the first callback and exceptions thrown by it, is serialized automatically by MCE::Hobo, using Sereal 3.008+ if installed or Storable otherwise.
The hoboprocess method is injected into Mojo::IOLoop.
hoboprocess
use Mojo::IOLoop; use Mojo::IOLoop::HoboProcess; my $subprocess = Mojo::IOLoop->hoboprocess( sub {...}, sub {...} ); my $subprocess = $loop->hoboprocess( sub {...}, sub {...} );
Build Mojo::IOLoop::HoboProcess object to perform computationally expensive operations in subprocesses, without blocking the event loop. Callbacks will be passed along to "run" in Mojo::IOLoop::HoboProcess.
# Concurrent subprocesses my ($fail, $result) = (); Mojo::IOLoop->delay( sub { my $delay = shift; Mojo::IOLoop->hoboprocess( sub {1}, $delay->begin ); Mojo::IOLoop->hoboprocess( sub {2}, $delay->begin ); }, sub { my ( $delay, $err1, $result1, $err2, $result2 ) = @_; $fail = $err1 || $err2; $result = [ $result1, $result2 ]; } )->wait;
The following is a variation of the synopsis above for demonstrating data sharing between subprocesses.
use Mojo::Cache; use Mojo::IOLoop::HoboProcess; use MCE::Shared; # Construct a shared cache and counter variable my $cache = MCE::Shared->share( Mojo::Cache->new( max_keys => 50 ) ); my $counter = MCE::Shared->scalar(0); # Also, construct a shared file handle mce_open my $OUT, ">", \*STDOUT or die "$!"; # Operations that would block the event loop for 3+ seconds my $subprocess1 = Mojo::IOLoop::HoboProcess->new; my $subprocess2 = Mojo::IOLoop::HoboProcess->new; $subprocess1->run( sub { my $subprocess = shift; say $OUT "Subprocess [$$] started"; $cache->set('key1', 'foo'); sleep 4; my $val = $cache->get('key2'); return "♥", "Mojolicious lots: $val: " . $counter->incr; }, sub { my ($subprocess, $err, @results) = @_; say $OUT "Subprocess error: $err" and return if $err; say $OUT "I $results[0] $results[1]!"; } ); $subprocess2->run( sub { my $subprocess = shift; say $OUT "Subprocess [$$] started"; $cache->set('key2', 'baz'); sleep 3; my $val = $cache->get('key1'); return "♥", "Mojolicious more: $val: " . $counter->incr; }, sub { my ($subprocess, $err, @results) = @_; say $OUT "Subprocess error: $err" and return if $err; say $OUT "I $results[0] $results[1]!"; } ); # Start event loop if necessary Mojo::IOLoop->start unless Mojo::IOLoop->is_running; __END__ Program output. Subprocess [7625] started Subprocess [7626] started I ♥ Mojolicious more: foo: 1! I ♥ Mojolicious lots: baz: 2!
A parallel demonstration for Recurring Monte Carlo Pi Calculation.
# Original source by Demian Riccardi and Joel Berger. # https://gist.github.com/dmr3/e69127ab449bdabd5af7b000c9b5b3b1 # # Parallel demonstration by Mario Roy. use Mojolicious::Lite; use MCE::Hobo; use MCE::Shared; any '/' => 'index'; websocket '/data' => sub { my $self = shift; my ($pi,$runs,$new_runs) = (0,0,1000000); my $timer = Mojo::IOLoop->recurring( 0.1 => sub { ($pi,$runs) = calc_pi($pi,$runs,$new_runs); $self->send({ json => [$runs,$pi] }); }); $self->on( finish => sub { Mojo::IOLoop->remove($timer); }); }; sub gen_data { my $x = shift; return [ $x, sin( $x + 2*rand() - 2*rand() ) ] } sub calc_pi { my ( $pi, $total_runs, $new_runs ) = @_; # use the itr to show how to submit multiples my $cnt_pi = MCE::Shared->scalar( -1 * ( ( $pi * $total_runs ) / 4 - $total_runs ) ); # shared sequence-generator my $seq = MCE::Shared->sequence( { chunk_size => 10000, bounds_only => 1 }, 1, $new_runs ); # Run my $routine = sub { while ( my ($beg, $end) = $seq->next ) { my ($cnt, $x, $y) = (0); foreach ( $beg .. $end ) { $x = rand(1); $y = rand(1); $cnt++ if $x * $x + $y * $y > 1; } $cnt_pi->incrby($cnt); } }; MCE::Hobo->create( $routine ) for ( 1 .. 3 ); MCE::Hobo->waitall; $total_runs += $new_runs; $pi = 4 * ( $total_runs - $cnt_pi->get ) / $total_runs; return ($pi, $total_runs); } app->start; __DATA__ @@ index.html.ep <!DOCTYPE html> <html> <head> <title><%= title %></title> </head> <body> %= content </body> <html> %= javascript 'https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js' %= javascript 'https://cdnjs.cloudflare.com/ajax/libs/flot/0.8.3/jquery.flot.js' <div id="plot" style="width:600px;height:300px"> </div> %= javascript begin var data = []; var plot = $.plot($('#plot'), [ data ]); var url = '<%= url_for('data')->to_abs %>'; var ws = new WebSocket( url ); ws.onmessage = function(e){ var point = JSON.parse(e.data); data.push(point); plot.setData([data]); plot.setupGrid(); plot.draw(); }; % end
Mojo::IOLoop::Subprocess was used as a template in the making of this module.
Mojo::IOLoop::Subprocess::Sereal was used as a template for the INSTALL file. As described above, MCE::Hobo already uses Sereal 3.008 or later automatically if available in Perl.
Mario E. Roy, <marioeroy AT gmail DOT com>
Copyright (C) 2016 by Mario E. Roy
Mojo::IOLoop::HoboProcess is released under the same license as Perl.
See http://dev.perl.org/licenses/ for more information.
Mojolicious, Mojolicious::Guides, http://mojolicious.org
MCE::Shared
To install Mojo::IOLoop::HoboProcess, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Mojo::IOLoop::HoboProcess
CPAN shell
perl -MCPAN -e shell install Mojo::IOLoop::HoboProcess
For more information on module installation, please visit the detailed CPAN module installation guide.