AnyEvent-Fork-RPC
view release on metacpan or search on metacpan
use AnyEvent;
use AnyEvent::Fork;
use AnyEvent::Fork::RPC;
my $done = AE::cv;
my $rpc = AnyEvent::Fork
->new
->require ("MyWorker")
->AnyEvent::Fork::RPC::run ("MyWorker::run",
on_error => sub { warn "ERROR: $_[0]"; exit 1 },
on_event => sub { warn "$_[0] requests handled\n" },
on_destroy => $done,
);
for my $id (1..6) {
$rpc->(rmdir => "/tmp/somepath/$id", sub {
$_[0]
or warn "/tmp/somepath/$id: $_[1]\n";
});
}
undef $rpc;
$done->recv;
The parent creates the process, queues a few rmdir's. It then forgets
about the $rpc object, so that the child exits after it has handled the
requests, and then it waits till the requests have been handled.
The child is implemented using a separate module, "MyWorker", shown
here:
package MyWorker;
my $count;
sub run {
my ($cmd, $path) = @_;
AnyEvent::Fork::RPC::event ($count)
unless ++$count % 3;
my $status = $cmd eq "rmdir" ? rmdir $path
: $cmd eq "unlink" ? unlink $path
: die "fatal error, illegal command '$cmd'";
$status or (0, "$!")
}
1
The "run" function first sends a "progress" event every three calls, and
then executes "rmdir" or "unlink", depending on the first parameter (or
dies with a fatal error - obviously, you must never let this happen :).
Eventually it returns the status value true if the command was
successful, or the status value 0 and the stringified error message.
On my system, running the first code fragment with the given MyWorker.pm
in the current directory yields:
/tmp/somepath/1: No such file or directory
/tmp/somepath/2: No such file or directory
3 requests handled
/tmp/somepath/3: No such file or directory
/tmp/somepath/4: No such file or directory
/tmp/somepath/5: No such file or directory
6 requests handled
/tmp/somepath/6: No such file or directory
Obviously, none of the directories I am trying to delete even exist.
Also, the events and responses are processed in exactly the same order
as they were created in the child, which is true for both synchronous
and asynchronous backends.
Note that the parentheses in the call to "AnyEvent::Fork::RPC::event"
are not optional. That is because the function isn't defined when the
code is compiled. You can make sure it is visible by pre-loading the
correct backend module in the call to "require":
->require ("AnyEvent::Fork::RPC::Sync", "MyWorker")
Since the backend module declares the "event" function, loading it first
ensures that perl will correctly interpret calls to it.
And as a final remark, there is a fine module on CPAN that can
asynchronously "rmdir" and "unlink" and a lot more, and more efficiently
than this example, namely IO::AIO.
Example 1a: the same with the asynchronous backend
This example only shows what needs to be changed to use the async
backend instead. Doing this is not very useful, the purpose of this
example is to show the minimum amount of change that is required to go
from the synchronous to the asynchronous backend.
To use the async backend in the previous example, you need to add the
"async" parameter to the "AnyEvent::Fork::RPC::run" call:
->AnyEvent::Fork::RPC::run ("MyWorker::run",
async => 1,
...
And since the function call protocol is now changed, you need to adopt
"MyWorker::run" to the async API.
First, you need to accept the extra initial $done callback:
sub run {
my ($done, $cmd, $path) = @_;
And since a response is now generated when $done is called, as opposed
to when the function returns, we need to call the $done function with
the status:
$done->($status or (0, "$!"));
A few remarks are in order. First, it's quite pointless to use the async
backend for this example - but it *is* possible. Second, you can call
$done before or after returning from the function. Third, having both
( run in 0.809 second using v1.01-cache-2.11-cpan-b16cb0d3907 )