AnyEvent-MP
view release on metacpan or search on metacpan
"recommends" : {
"Net::Interface" : "1.011"
},
"requires" : {
"AnyEvent" : "6.14",
"AnyEvent::Watchdog" : "1",
"CBOR::XS" : "1.5",
"Digest::HMAC" : "1.03",
"Digest::SHA3" : "0.24",
"Guard" : "1.022",
"JSON::XS" : "2.25",
"MIME::Base64" : "3",
"Task::Weaken" : "0",
"common::sense" : "0"
}
}
},
"release_status" : "stable",
"version" : "2.02",
"x_serialization_backend" : "JSON::PP version 2.27300"
}
- inc
recommends:
Net::Interface: '1.011'
requires:
AnyEvent: '6.14'
AnyEvent::Watchdog: '1'
CBOR::XS: '1.5'
Digest::HMAC: '1.03'
Digest::SHA3: '0.24'
Guard: '1.022'
JSON::XS: '2.25'
MIME::Base64: '3'
Task::Weaken: '0'
common::sense: '0'
version: '2.02'
x_serialization_backend: 'CPAN::Meta::YAML version 0.012'
And as a result, a port with just a default receiver consists of a single
code reference stored in a global hash - it can't become much cheaper.
=item Why favour JSON, why not a real serialising format such as Storable?
In fact, any AnyEvent::MP node will happily accept Storable as framing
format, but currently there is no way to make a node use Storable by
default (although all nodes will accept it).
The default framing protocol is JSON because a) JSON::XS is many times
faster for small messages and b) most importantly, after years of
experience we found that object serialisation is causing more problems
than it solves: Just like function calls, objects simply do not travel
easily over the network, mostly because they will always be a copy, so you
always have to re-think your design.
Keeping your messages simple, concentrating on data structures rather than
objects, will keep your messages clean, tidy and efficient.
=back
MP/Config.pm view on Meta::CPAN
Move along please, nothing to see here at the moment.
=cut
package AnyEvent::MP::Config;
use common::sense;
use Carp ();
use AnyEvent ();
use JSON::XS ();
our $VERSION = '2.02';
our $CONFIG_FILE = exists $ENV{PERL_ANYEVENT_MP_RC} ? $ENV{PERL_ANYEVENT_MP_RC}
: exists $ENV{HOME} ? "$ENV{HOME}/.perl-anyevent-mp"
: "$ENV{APPDATA}/perl-anyevent-mp";
our %CFG;
sub load {
if (open my $fh, "<:raw", $CONFIG_FILE) {
return if eval {
local $/;
%CFG = %{ JSON::XS->new->utf8->relaxed->decode (scalar <$fh>) };
1
};
}
%CFG = (
version => 1,
);
}
sub save {
return unless delete $CFG{dirty};
open my $fh, ">:raw", "$CONFIG_FILE~new~"
or Carp::croak "$CONFIG_FILE~new~: $!";
syswrite $fh, JSON::XS->new->pretty->utf8->encode (\%CFG) . "\n"
or Carp::croak "$CONFIG_FILE~new~: $!";
close $fh
or Carp::croak "$CONFIG_FILE~new~: $!";
unlink "$CONFIG_FILE~";
link $CONFIG_FILE, "$CONFIG_FILE~";
rename "$CONFIG_FILE~new~", $CONFIG_FILE
or Carp::croak "$CONFIG_FILE: $!";
}
MP/Global.pm view on Meta::CPAN
use AnyEvent::MP;
use AnyEvent::MP::Kernel;
AE::log 7 => "starting global service.";
#############################################################################
# node protocol parts for global nodes
package AnyEvent::MP::Kernel;
use JSON::XS ();
# TODO: this is ugly (classical use vars vs. our),
# maybe this should go into MP::Kernel
# "import" from Kernel
our %NODE;
our $NODE;
#our $GLOBAL;
our $SRCNODE; # the origin node id
our %NODE_REQ;
MP/Kernel.pm view on Meta::CPAN
During execution of a message callback, this variable contains the node ID
of the origin node.
The main use of this variable is for debugging output - there are probably
very few other cases where you need to know the source node ID.
=cut
sub _inject {
warn "RCV $SRCNODE -> " . eval { JSON::XS->new->encode (\@_) } . "\n" if TRACE && @_;
&{ $PORT{+shift} or return };
}
# this function adds a node-ref, so you can send stuff to it
# it is basically the central routing component.
sub add_node {
$NODE{$_[0]} || do {
my ($node) = @_;
MP/Kernel.pm view on Meta::CPAN
or Carp::croak "'undef' or the empty string are not valid node/port IDs";
# registers itself in %NODE
new AnyEvent::MP::Node::Remote $node
}
}
sub snd(@) {
my ($nodeid, $portid) = split /#/, shift, 2;
warn "SND $nodeid <- " . eval { JSON::XS->new->encode ([$portid, @_]) } . "\n" if TRACE && @_;
($NODE{$nodeid} || add_node $nodeid)
->{send} (["$portid", @_]);
}
sub port_is_local($) {
my ($nodeid, undef) = split /#/, $_[0], 2;
$nodeid eq $NODE
}
MP/Kernel.pm view on Meta::CPAN
$NODE_REQ{g_slave} = sub {
# load global module and redo the request
require AnyEvent::MP::Global;
&{ $NODE_REQ{g_slave} }
};
#############################################################################
# local database operations
# canonical probably not needed
our $sv_eq_coder = JSON::XS->new->utf8->allow_nonref;
# are the two scalars equal? very very ugly and slow, need better way
sub sv_eq($$) {
ref $_[0] || ref $_[1]
? (JSON::XS::encode $sv_eq_coder, $_[0]) eq (JSON::XS::encode $sv_eq_coder, $_[1])
: $_[0] eq $_[1]
&& defined $_[0] == defined $_[1]
}
# local database management
sub db_del($@) {
my $family = shift;
my @del = grep exists $LOCAL_DB{$family}{$_}, @_;
MP/Transport.pm view on Meta::CPAN
my $framing = $self->{s_framing};
my $hdl = $self->{hdl};
my $push_write = $hdl->can ("push_write");
if ($framing eq "cbor") {
require CBOR::XS;
$self->{send} = sub {
$push_write->($hdl, CBOR::XS::encode_cbor ($_[0]));
};
} elsif ($framing eq "json") {
require JSON::XS;
$self->{send} = sub {
$push_write->($hdl, JSON::XS::encode_json ($_[0]));
};
} else {
$self->{send} = sub {
$push_write->($hdl, $framing => $_[0]);
};
}
}
sub set_rcv_framing {
my ($self) = @_;
MP/Transport.pm view on Meta::CPAN
$hdl->on_read (sub {
$AnyEvent::MP::Kernel::SRCNODE = $node;
AnyEvent::MP::Kernel::_inject (@$_)
for $coder->incr_parse_multiple ($_[0]{rbuf});
()
});
} elsif ($framing eq "json") {
require JSON::XS;
my $coder = JSON::XS->new->utf8;
$hdl->on_read (sub {
$AnyEvent::MP::Kernel::SRCNODE = $node;
AnyEvent::MP::Kernel::_inject (@$_)
for $coder->incr_parse (delete $_[0]{rbuf});
()
});
} else {
Makefile.PL view on Meta::CPAN
NAME => "AnyEvent::MP",
VERSION_FROM => "MP/Config.pm",
EXE_FILES => ["bin/aemp"],
CONFIGURE_REQUIRES => { ExtUtils::MakeMaker => 6.52, Canary::Stability => 0 },
PREREQ_PM => {
AnyEvent => 6.14,
AnyEvent::Watchdog => 1.0,
Digest::SHA3 => 0.24,
Digest::HMAC => 1.03,
MIME::Base64 => 3,
JSON::XS => 2.25,
CBOR::XS => 1.5,
Guard => 1.022,
common::sense => 0,
Task::Weaken => 0,
},
META_MERGE => {
recommends => {
Net::Interface => 1.011,
}
},
And as a result, a port with just a default receiver consists of a
single code reference stored in a global hash - it can't become much
cheaper.
Why favour JSON, why not a real serialising format such as Storable?
In fact, any AnyEvent::MP node will happily accept Storable as
framing format, but currently there is no way to make a node use
Storable by default (although all nodes will accept it).
The default framing protocol is JSON because a) JSON::XS is many
times faster for small messages and b) most importantly, after years
of experience we found that object serialisation is causing more
problems than it solves: Just like function calls, objects simply do
not travel easily over the network, mostly because they will always
be a copy, so you always have to re-think your design.
Keeping your messages simple, concentrating on data structures
rather than objects, will keep your messages clean, tidy and
efficient.
=back
=cut
use common::sense;
# should come before anything else, so all modules
# will be loaded on each restart
BEGIN {
if (@ARGV == 1 && $ARGV[0] =~ /^\[/) {
require JSON::XS;
@ARGV = @{ JSON::XS->new->utf8->decode (shift) };
} else {
for (@ARGV) {
if (/^[\[\{\"]/) {
require JSON::XS;
$_ = JSON::XS->new->utf8->allow_nonref->decode ($_);
}
}
}
if ($ARGV[0] eq "run") {
shift;
# d'oh
require AnyEvent::Watchdog;
# only now can we load additional modules
AnyEvent::MP::Kernel::configure (@ARGV);
AnyEvent::detect () eq "AnyEvent::Impl::EV"
? EV::loop ()
: AE::cv ()->recv;
}
}
use Carp ();
use JSON::XS;
use AnyEvent;
use AnyEvent::Util;
use AnyEvent::MP;
use AnyEvent::MP::Config;
sub my_run_cmd {
my ($cmd) = @_;
print <<EOF;
Entering interactive shell - no commandline editing of course (use rlfe etc.).
\= display a list of nodes
\=name switch to another node
package P switch to package P when evaluating
\$ECHO contains the name of a port that echos everything sent to it
EOF
my $json = JSON::XS->new->pretty->ascii;
my $pkg = "AnyEvent::MP::Kernel";
my $cv = AE::cv;
my $echo = port {
print "\nECHO<$AnyEvent::MP::Kernel::SRCNODE> ", $json->encode (\@_), "\n$node $pkg> ";
};
print "$node $pkg> ";
my $t = AE::io *STDIN, 0, sub {
chomp (my $line = <STDIN>);
if ($line =~ s/^=//) {
my $cv = AE::cv;
my $to = AE::timer 5, 0, sub { exit 1 };
AnyEvent::MP::Kernel::eval_on $node, $expr, port { &$cv };
mon $node, $cv;
my ($err, @res) = $cv->recv;
die "$err @res" if length $err;
print +(substr JSON::XS->new->encode (\@res), 1, -1), "\n";
}
sub docmd;
our %CMD = (
snd => sub {
my $port = shift @ARGV;
init;
snd $port, @ARGV; @ARGV = ();
print join " ", $cv->recv, "\n";
},
cal => sub {
my $port = shift @ARGV;
init;
my $cv = AE::cv;
cal $port, @ARGV, sub { &$cv }; @ARGV = ();
print +(substr JSON::XS->new->encode ([$cv->recv]), 1, -1), "\n";
},
mon => sub {
my $port = shift @ARGV;
init;
mon $port, my $cv = AE::cv;
print join " ", $cv->recv, "\n";
},
},
delparent => sub {
delete $profile->{parent};
++$cfg->{dirty};
},
showprofile => sub {
@ARGV >= 1
or die "profile name is missing\n";
my $name = shift @ARGV;
print JSON::XS->new->pretty->encode ($cfg->{profile}{$name} || {});
},
showconfig => sub {
my $name = @ARGV ? shift @ARGV : AnyEvent::MP::Kernel::nodename;
my $profile = AnyEvent::MP::Config::find_profile $name, @ARGV;
@ARGV = ();
# make it look nicer:
delete $profile->{profile};
delete $profile->{parent};
print JSON::XS->new->pretty->encode ($profile);
},
);
for my $attr (qw(
monitor_timeout connect_interval framing_format auth_offer
auth_accept autocork nodelay secure
)) {
$CMD{"set$attr"} = sub {
@ARGV >= 1
or die "$attr value is missing\n";
( run in 0.553 second using v1.01-cache-2.11-cpan-4d50c553e7e )