--- AnyEvent-Fork-RPC/RPC.pm 2013/08/25 22:21:15 1.30 +++ AnyEvent-Fork-RPC/RPC.pm 2013/11/20 16:17:22 1.35 @@ -177,7 +177,7 @@ =head2 Example 2: Asynchronous Backend This example implements multiple count-downs in the child, using -L timers. While this is a bit silly (one could use timers in te +L timers. While this is a bit silly (one could use timers in the parent just as well), it illustrates the ability to use AnyEvent in the child and the fact that responses can arrive in a different order then the requests. @@ -393,7 +393,7 @@ use AnyEvent; -our $VERSION = 1.1; +our $VERSION = 1.21; =item my $rpc = AnyEvent::Fork::RPC::run $fork, $function, [key => value...] @@ -456,6 +456,20 @@ used to load any modules that provide the serialiser or function. It can not, however, create events. +=item done => $function (default C) + +The function to call when the asynchronous backend detects an end of file +condition when reading from the communications socket I there are no +outstanding requests. It's ignored by the synchronous backend. + +By overriding this you can prolong the life of a RPC process after e.g. +the parent has exited by running the event loop in the provided function +(or simply calling it, for example, when your child process uses L you +could provide L as C function). + +Of course, in that case you are responsible for exiting at the appropriate +time and not returning from + =item async => $boolean (default: 0) The default server used in the child does all I/O blockingly, and only @@ -513,6 +527,30 @@ sub { unpack "(w/a*)*", shift } ) +=item json - C<$AnyEvent::Fork::RPC::CBOR_XS_SERIALISER> + +This serialiser creates CBOR::XS arrays - you have to make sure the +L module is installed for this serialiser to work. It can be +beneficial for sharing when you preload the L module in a template +process. + +L is about as fast as the octet string serialiser, but supports +complex data structures (similar to JSON) and is faster than any of the +other serialisers. If you have the L module available, it's the +best choice. + +Note that the CBOR::XS module supports some extensions to encode cyclic +and self-referencing data structures, which are not enabled. You need to +write your own serialiser to take advantage of these. + +Implementation: + + use CBOR::XS (); + ( + sub { CBOR::XS::encode_cbor \@_ }, + sub { @{ CBOR::XS::decode_cbor shift } } + ) + =item json - C<$AnyEvent::Fork::RPC::JSON_SERIALISER> This serialiser creates JSON arrays - you have to make sure the L @@ -571,7 +609,8 @@ =cut our $STRING_SERIALISER = '(sub { pack "(w/a*)*", @_ }, sub { unpack "(w/a*)*", shift })'; -our $JSON_SERIALISER = 'use JSON (); (sub { JSON::encode_json \@_ }, sub { @{ JSON::decode_json shift } })'; +our $CBOR_XS_SERIALISER = 'use CBOR::XS (); (sub { CBOR::XS::encode_cbor \@_ }, sub { @{ CBOR::XS::decode_cbor shift } })'; +our $JSON_SERIALISER = 'use JSON (); (sub { JSON::encode_json \@_ }, sub { @{ JSON::decode_json shift } })'; our $STORABLE_SERIALISER = 'use Storable (); (sub { Storable::freeze \@_ }, sub { @{ Storable::thaw shift } })'; our $NSTORABLE_SERIALISER = 'use Storable (); (sub { Storable::nfreeze \@_ }, sub { @{ Storable::thaw shift } })'; @@ -617,7 +656,7 @@ my $module = "AnyEvent::Fork::RPC::" . ($arg{async} ? "Async" : "Sync"); $self->require ($module) - ->send_arg ($function, $arg{init}, $serialiser) + ->send_arg ($function, $arg{init}, $serialiser, $arg{done} || "$module\::do_exit") ->run ("$module\::run", sub { $fh = shift; @@ -743,6 +782,56 @@ =back +=head2 PROCESS EXIT + +If and when the child process exits depends on the backend and +configuration. Apart from explicit exits (e.g. by calling C) or +runtime conditions (uncaught exceptions, signals etc.), the backends exit +under these conditions: + +=over 4 + +=item Synchronous Backend + +The synchronous backend is very simple: when the process waits for another +request to arrive and the writing side (usually in the parent) is closed, +it will exit normally, i.e. as if your main program reached the end of the +file. + +That means that if your parent process exits, the RPC process will usually +exit as well, either because it is idle anyway, or because it executes a +request. In the latter case, you will likely get an error when the RPc +process tries to send the results to the parent (because agruably, you +shouldn't exit your parent while there are still outstanding requests). + +The process is usually quiescent when it happens, so it should rarely be a +problem, and C handlers can be used to clean up. + +=item Asynchronous Backend + +For the asynchronous backend, things are more complicated: Whenever it +listens for another request by the parent, it might detect that the socket +was closed (e.g. because the parent exited). It will sotp listening for +new requests and instead try to write out any remaining data (if any) or +simply check whether the socket can be written to. After this, the RPC +process is effectively done - no new requests are incoming, no outstanding +request data can be written back. + +Since chances are high that there are event watchers that the RPC server +knows nothing about (why else would one use the async backend if not for +the ability to register watchers?), the event loop would often happily +continue. + +This is why the asynchronous backend explicitly calls C when +it is done (under other circumstances, such as when there is an I/O error +and there is outstanding data to write, it will log a fatal message via +L, also causing the program to exit). + +You can override this by specifying a function name to call via the C +parameter instead. + +=back + =head1 ADVANCED TOPICS =head2 Choosing a backend