ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent-Fork-RPC/RPC.pm
(Generate patch)

Comparing AnyEvent-Fork-RPC/RPC.pm (file contents):
Revision 1.11 by root, Thu Apr 18 07:59:46 2013 UTC vs.
Revision 1.12 by root, Thu Apr 18 10:40:34 2013 UTC

579See the examples section earlier in this document for some actual 579See the examples section earlier in this document for some actual
580examples. 580examples.
581 581
582=back 582=back
583 583
584=head1 ADVANCED TOPICS
585
586=head2 Choosing a backend
587
588So how do you decide which backend to use? Well, that's your problem to
589solve, but here are some thoughts on the matter:
590
591=over 4
592
593=item Synchronous
594
595The synchronous backend does not rely on any external modules (well,
596except L<common::sense>, which works around a bug in how perl's warning
597system works). This keeps the process very small, for example, on my
598system, an empty perl interpreter uses 1492kB RSS, which becomes 2020kB
599after C<use warnings; use strict> (for people who grew up with C64s around
600them this is probably shocking every single time they see it). The worker
601process in the first example in this document uses 1792kB.
602
603Since the calls are done synchronously, slow jobs will keep newer jobs
604from executing.
605
606The synchronous backend also has no overhead due to running an event loop
607- reading requests is therefore very efficient, while writing responses is
608less so, as every response results in a write syscall.
609
610If the parent process is busy and a bit slow reading responses, the child
611waits instead of processing further requests. This also limits the amount
612of memory needed for buffering, as never more than one response has to be
613buffered.
614
615The API in the child is simple - you just have to define a function that
616does something and returns something.
617
618It's hard to use modules or code that relies on an event loop, as the
619child cannot execute anything while it waits for more input.
620
621=item Asynchronous
622
623The asynchronous backend relies on L<AnyEvent>, which tries to be small,
624but still comes at a price: On my system, the worker from example 1a uses
6253420kB RSS (for L<AnyEvent>, which loads L<EV>, which needs L<XSLoader>
626which in turn loads a lot of other modules such as L<warnings>, L<strict>,
627L<vars>, L<Exporter>...).
628
629It batches requests and responses reasonably efficiently, doing only as
630few reads and writes as needed, but needs to poll for events via the event
631loop.
632
633Responses are queued when the parent process is busy. This means the child
634can continue to execute any queued requests. It also means that a child
635might queue a lot of responses in memory when it generates them and the
636parent process is slow accepting them.
637
638The API is not a straightforward RPC pattern - you have to call a
639"done" callback to pass return values and signal completion. Also, more
640importantly, the API starts jobs as fast as possible - when 1000 jobs
641are queued and the jobs are slow, they will all run concurrently. The
642child must implement some queueing/limiting mechanism if this causes
643problems. Alternatively, the parent could limit the amount of rpc calls
644that are outstanding.
645
646Using event-based modules such as L<IO::AIO>, L<Gtk2>, L<Tk> and so on is
647easy.
648
649=back
650
651=head2 Passing file descriptors
652
653Unlike L<AnyEvent::Fork>, this module has no in-built file handle or file
654descriptor passing abilities.
655
656The reason is that passing file descriptors is extraordinary tricky
657business, and conflicts with efficient batching of messages.
658
659There still is a method you can use: Create a
660C<AnyEvent::Util::portable_socketpair> and C<send_fh> one half of it to
661the process before you pass control to C<AnyEvent::Fork::RPC::run>.
662
663Whenever you want to pass a file descriptor, send an rpc request to the
664child process (so it expects the descriptor), then send it over the other
665half of the socketpair. The child should fetch the descriptor from the
666half it has passed earlier.
667
668Here is some (untested) pseudocode to that effect:
669
670 use AnyEvent::Util;
671 use AnyEvent::Fork::RPC;
672 use IO::FDPass;
673
674 my ($s1, $s2) = AnyEvent::Util::portable_socketpair;
675
676 my $rpc = AnyEvent::Fork
677 ->new
678 ->send_fh ($s2)
679 ->require ("MyWorker")
680 ->AnyEvent::Fork::RPC::run ("MyWorker::run"
681 init => "MyWorker::init",
682 );
683
684 undef $s2; # no need to keep it around
685
686 # pass an fd
687 $rpc->("i'll send some fd now, please expect it!", my $cv = AE::cv);
688
689 IO::FDPass fileno $s1, fileno $handle_to_pass;
690
691 $cv->recv;
692
693The MyWorker module could look like this:
694
695 package MyWorker;
696
697 use IO::FDPass;
698
699 my $s2;
700
701 sub init {
702 $s2 = $_[0];
703 }
704
705 sub run {
706 if ($_[0] eq "i'll send some fd now, please expect it!") {
707 my $fd = IO::FDPass::recv fileno $s2;
708 ...
709 }
710 }
711
712Of course, this might be blocking if you pass a lot of file descriptors,
713so you might want to look into L<AnyEvent::FDpasser> which can handle the
714gory details.
715
584=head1 SEE ALSO 716=head1 SEE ALSO
585 717
586L<AnyEvent::Fork> (to create the processes in the first place), 718L<AnyEvent::Fork> (to create the processes in the first place),
587L<AnyEvent::Fork::Pool> (to manage whole pools of processes). 719L<AnyEvent::Fork::Pool> (to manage whole pools of processes).
588 720

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines