Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Commit experimental things for others to consider.
- Loading branch information
Showing
9 changed files
with
463 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package Sidecar; | ||
# vim: ts=2 sw=2 noexpandtab | ||
|
||
# "Sidecar" is what I call a subprocess that handles a particular | ||
# object. The analogy is to motorcycle sidecars. | ||
|
||
use warnings; | ||
use strict; | ||
|
||
use Storable qw(nfreeze thaw); | ||
|
||
sub BUILD { | ||
use IPC::Run qw(start); | ||
use Symbol qw(gensym); | ||
|
||
my ($fh_in, $fh_out, $fh_err) = (gensym(), gensym(), gensym()); | ||
|
||
my $ipc_run = start( | ||
$cmd, | ||
'<pipe', $fh_in, | ||
'>pipe', $fh_out, | ||
'2>pipe', $fh_err, | ||
) or die "IPC::Run start() failed: $? ($!)"; | ||
|
||
return($ipc_run, $fh_in, $fh_out, $fh_err); | ||
} | ||
|
||
sub _sidecar_drive { | ||
my $self = shift; | ||
|
||
my $buffer = ""; | ||
my $read_length; | ||
|
||
binmode(STDIN); | ||
binmode(STDOUT); | ||
select STDOUT; $| = 1; | ||
|
||
use bytes; | ||
|
||
while (1) { | ||
if (defined $read_length) { | ||
if (length($buffer) >= $read_length) { | ||
my $request = thaw(substr($buffer, 0, $read_length, "")); | ||
$read_length = undef; | ||
|
||
my ($request_id, $context, $method, @args) = @$request; | ||
|
||
my $streamable; | ||
|
||
if ($context eq "array") { | ||
my (@return) = eval { $self->$method(@args); }; | ||
$streamable = nfreeze( [ $request_id, $context, $@, @return ] ); | ||
} | ||
elsif ($context eq "scalar") { | ||
my $return = eval { $self->$method(@args); }; | ||
$streamable = nfreeze( [ $request_id, $context, $@, $return ] ); | ||
} | ||
else { | ||
eval { $self->$method(@args); undef; }; | ||
$streamable = nfreeze( [ $request_id, $context, $@ ] ); | ||
} | ||
|
||
my $stream = length($streamable) . chr(0) . $streamable; | ||
|
||
my $octets_wrote = syswrite(STDOUT, $stream); | ||
die $! unless $octets_wrote == length($stream); | ||
|
||
next; | ||
} | ||
} | ||
elsif ($buffer =~ s/^(\d+)\0//) { | ||
$read_length = $1; | ||
next; | ||
} | ||
|
||
my $octets_read = sysread(STDIN, $buffer, 4096, length($buffer)); | ||
last unless $octets_read; | ||
} | ||
|
||
exit 0; | ||
} | ||
|
||
1; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/usr/bin/env perl | ||
# vim: ts=2 sw=2 noexpandtab | ||
|
||
use warnings; | ||
use strict; | ||
use lib qw(./lib ../lib ./eg); | ||
|
||
{ | ||
package Foo; | ||
use Moose; | ||
extends 'Reflex::Timeout'; | ||
use ExampleHelpers qw(eg_say); | ||
|
||
sub on_done { | ||
eg_say "custom got timeout"; | ||
$_[0]->reset(); | ||
} | ||
} | ||
|
||
my $to = Foo->new(delay => 1); | ||
Reflex->run_all(); | ||
exit; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
# vim: ts=2 sw=2 noexpandtab | ||
# This is a quick, one-off implementation of a one-shot worker pool. | ||
# Give it some jobs, and it'll run them all in parallel. It will | ||
# return results in the order of completion. | ||
# | ||
# It doesn't use the proposed collection promise. | ||
# It doesn't limit simultaneous workers. | ||
# It doesn't implement a generic Enterprise Integration Pattern. | ||
# In short, it does almost nothing generically useful. | ||
# | ||
# It does, however, act as an example of Reflex::POE::Wheel::Run used | ||
# for a practical purpose. | ||
|
||
use lib qw(../lib); | ||
|
||
# Start a parallel runner with a list of jobs. | ||
# ParallelRunner's implementation is below. | ||
|
||
my $pr = ParallelRunner->new( | ||
jobs => [ | ||
[ \&adder, 1, 2, 3 ], | ||
[ \&multiplier, 4, 5, 6 ], | ||
] | ||
); | ||
|
||
# Consume results until we're done. | ||
|
||
while (my $event = $pr->next()) { | ||
use YAML; | ||
print YAML::Dump($event->{arg}{result}); | ||
} | ||
|
||
exit; | ||
|
||
# Jobs to run. | ||
|
||
sub adder { | ||
use Time::HiRes qw(sleep); sleep rand(); | ||
|
||
my $accumulator = 0; | ||
$accumulator += $_ foreach @_; | ||
return [ adder => $accumulator ]; | ||
} | ||
|
||
sub multiplier { | ||
use Time::HiRes qw(sleep); sleep rand(); | ||
|
||
my $accumulator = 1; | ||
$accumulator *= $_ foreach @_; | ||
return [ multiplier => $accumulator ]; | ||
} | ||
|
||
# Implementation of the ParallelRunner. | ||
|
||
BEGIN { | ||
package ParallelRunner; | ||
|
||
use Moose; | ||
extends 'Reflex::Base'; | ||
use Reflex::Collection; | ||
use Reflex::POE::Wheel::Run; | ||
use Reflex::Callbacks; | ||
|
||
use POE::Filter::Line; | ||
use POE::Filter::Reference; | ||
|
||
has jobs => ( | ||
isa => 'ArrayRef[ArrayRef]', | ||
is => 'ro', | ||
); | ||
|
||
has_many workers => ( handles => { remember_worker => "remember" } ); | ||
|
||
sub BUILD { | ||
my ($self, $args) = @_; | ||
|
||
foreach my $job (@{$self->jobs()}) { | ||
my ($coderef, @parameters) = @$job; | ||
|
||
$self->remember_worker( | ||
Reflex::POE::Wheel::Run->new( | ||
Program => sub { | ||
my $f = POE::Filter::Reference->new(); | ||
my $output = $f->put( [ $coderef->(@parameters) ] ); | ||
syswrite(STDOUT, $_) foreach @$output; | ||
close STDOUT; | ||
}, | ||
StdoutFilter => POE::Filter::Reference->new(), | ||
cb_role($self, "child"), | ||
) | ||
); | ||
} | ||
} | ||
|
||
sub on_child_stderr { | ||
warn "child reported: $_[1]{output}\n"; | ||
} | ||
|
||
sub on_child_stdout { | ||
my ($self, $args) = @_; | ||
|
||
$self->emit( | ||
event => 'result', | ||
args => { result => $args->{output} }, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
#!/usr/bin/perl | ||
# vim: ts=2 sw=2 noexpandtab | ||
|
||
use strict; | ||
use warnings; | ||
use feature qw( say ); | ||
|
||
use Socket qw( AF_INET unpack_sockaddr_in inet_ntoa ); | ||
use Socket::GetAddrInfo qw( :newapi getaddrinfo ); | ||
|
||
sub format_addr { | ||
my ($port, $inaddr) = unpack_sockaddr_in $_[0]; | ||
sprintf "%s:%d", inet_ntoa($inaddr), $port; | ||
} | ||
|
||
use POE qw( Session Kernel Wheel::ReadWrite Wheel::Run Filter::Reference ); | ||
|
||
{ | ||
my $wheel_resolver; | ||
|
||
POE::Session->create( | ||
inline_states => { | ||
_start => sub { | ||
$wheel_resolver = POE::Wheel::Run->new( | ||
Program => sub { | ||
my ($err, @addrs) = | ||
getaddrinfo("localhost", "www", {family => AF_INET}); | ||
die "$err" if $err; | ||
print @{POE::Filter::Reference->new->put([$addrs[0]])}; | ||
}, | ||
StdoutFilter => POE::Filter::Reference->new, | ||
StdoutEvent => 'resolver_input', | ||
StderrEvent => 'resolver_error', | ||
); | ||
}, | ||
|
||
resolver_input => | ||
sub { say "POE resolved " . format_addr($_[ARG0]->{addr}) }, | ||
resolver_error => sub { say "POE resolver error $_[ARG0]" }, | ||
}, | ||
); | ||
} | ||
|
||
POE::Kernel->run; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
#!/usr/bin/env perl | ||
# vim: ts=2 sw=2 noexpandtab | ||
|
||
use Moose; | ||
use Sidecar; | ||
|
||
{ | ||
package BlockingService; | ||
|
||
use Moose; | ||
|
||
has counter => ( is => 'rw', isa => 'Int', default => 0 ); | ||
|
||
sub next { | ||
my $self = shift; | ||
|
||
return "pid($$) counter = " . $self->counter( $self->counter() + 1 ); | ||
} | ||
} | ||
|
||
my $scbs = Sidecar->new(class => 'BlockingService'); | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#!/usr/bin/perl | ||
# vim: ts=2 sw=2 noexpandtab | ||
|
||
use warnings; | ||
use strict; | ||
use lib qw(../lib); | ||
|
||
# Demonstrate how wheels may be encapsulated in thin, | ||
# configuration-only subclasses. | ||
|
||
{ | ||
package Runner; | ||
use Moose; | ||
extends 'Reflex::Base'; | ||
use Reflex::POE::Wheel::Run; | ||
use Reflex::Trait::Watched qw(watches); | ||
|
||
watches child => ( | ||
isa => 'Maybe[Reflex::POE::Wheel::Run]', | ||
); | ||
|
||
sub BUILD { | ||
my $self = shift; | ||
$self->child( | ||
Reflex::POE::Wheel::Run->new( | ||
Program => "$^X -wle '\$|=1; while (<STDIN>) { chomp; print qq[pid(\$\$) moo(\$_)] } exit'", | ||
) | ||
); | ||
|
||
$self->child()->put("one", "two", "three", "last"); | ||
} | ||
|
||
sub on_child_stdin { | ||
print "stdin flushed\n"; | ||
} | ||
|
||
sub on_child_stdout { | ||
my ($self, $args) = @_; | ||
print "stdout: $args->{output}\n"; | ||
$self->child()->kill() if $args->{output} =~ /moo\(last\)/; | ||
} | ||
|
||
sub on_child_stderr { | ||
my ($self, $args) = @_; | ||
print "stderr: $args->{output}\n"; | ||
} | ||
|
||
sub on_child_error { | ||
my ($self, $args) = @_; | ||
return if $args->{operation} eq "read"; | ||
print "$args->{operation} error $args->{errnum}: $args->{errstr}\n"; | ||
} | ||
|
||
sub on_child_close { | ||
my ($self, $args) = @_; | ||
print "child closed all output\n"; | ||
} | ||
|
||
sub on_child_signal { | ||
my ($self, $args) = @_; | ||
print "child $args->{pid} exited: $args->{exit}\n"; | ||
$self->child(undef); | ||
} | ||
} | ||
|
||
# Main. | ||
|
||
my $runner = Runner->new(); | ||
Reflex->run_all(); | ||
exit; |
Oops, something went wrong.