AnyEvent-Fork-RPC

 view release on metacpan or  search on metacpan

RPC.pm  view on Meta::CPAN

   job 2 finished
   count 2 of 3
   count 3 of 3
   job 3 finished

While the overall ordering isn't guaranteed, the async backend still
guarantees that events and responses are delivered to the parent process
in the exact same ordering as they were generated in the child process.

And unless your system is I<very> busy, it should clearly show that the
job started last will finish first, as it has the lowest count.

This concludes the async example. Since L<AnyEvent::Fork> does not
actually fork, you are free to use about any module in the child, not just
L<AnyEvent>, but also L<IO::AIO>, or L<Tk> for example.

=head2 Example 3: Asynchronous backend with Coro

With L<Coro> you can create a nice asynchronous backend implementation by
defining an rpc server function that creates a new Coro thread for every
request that calls a function "normally", i.e. the parameters from the
parent process are passed to it, and any return values are returned to the
parent process, e.g.:

   package My::Arith;

   sub add {
      return $_[0] + $_[1];
   }

   sub mul {
      return $_[0] * $_[1];
   }

   sub run {
      my ($done, $func, @arg) = @_;

      Coro::async_pool {
         $done->($func->(@arg));
      };
   }

The C<run> function creates a new thread for every invocation, using the
first argument as function name, and calls the C<$done> callback on it's
return values. This makes it quite natural to define the C<add> and C<mul>
functions to add or multiply two numbers and return the result.

Since this is the asynchronous backend, it's quite possible to define RPC
function that do I/O or wait for external events - their execution will
overlap as needed.

The above could be used like this:

   my $rpc = AnyEvent::Fork
      ->new
      ->require ("MyWorker")
      ->AnyEvent::Fork::RPC::run ("My::Arith::run",
         on_error => ..., on_event => ..., on_destroy => ...,
      );

   $rpc->(add => 1, 3, Coro::rouse_cb); say Coro::rouse_wait;
   $rpc->(mul => 3, 2, Coro::rouse_cb); say Coro::rouse_wait;

The C<say>'s will print C<4> and C<6>.

=head2 Example 4: Forward AnyEvent::Log messages using C<on_event>

This partial example shows how to use the C<event> function to forward
L<AnyEvent::Log> messages to the parent.

For this, the parent needs to provide a suitable C<on_event>:

   ->AnyEvent::Fork::RPC::run (
      on_event => sub {
         if ($_[0] eq "ae_log") {
            my (undef, $level, $message) = @_;
            AE::log $level, $message;
         } else {
            # other event types
         }
      },
   )

In the child, as early as possible, the following code should reconfigure
L<AnyEvent::Log> to log via C<AnyEvent::Fork::RPC::event>:

   $AnyEvent::Log::LOG->log_cb (sub {
      my ($timestamp, $orig_ctx, $level, $message) = @{+shift};

      if (defined &AnyEvent::Fork::RPC::event) {
         AnyEvent::Fork::RPC::event (ae_log => $level, $message);
      } else {
         warn "[$$ before init] $message\n";
      }
   });

There is an important twist - the C<AnyEvent::Fork::RPC::event> function
is only defined when the child is fully initialised. If you redirect the
log messages in your C<init> function for example, then the C<event>
function might not yet be available. This is why the log callback checks
whether the function is there using C<defined>, and only then uses it to
log the message.

=head1 PARENT PROCESS USAGE

This module exports nothing, and only implements a single function:

=over 4

=cut

package AnyEvent::Fork::RPC;

use common::sense;

use Errno ();
use Guard ();

use AnyEvent;

our $VERSION = '2.0';



( run in 0.325 second using v1.01-cache-2.11-cpan-483215c6ad5 )