--- AnyEvent/lib/AnyEvent.pm 2005/12/01 21:19:58 1.3 +++ AnyEvent/lib/AnyEvent.pm 2005/12/19 17:03:29 1.6 @@ -8,24 +8,34 @@ use AnyEvent; - my $w = AnyEvent->timer (fh => ..., poll => "[rw]+", cb => sub { + my $w = AnyEvent->io (fh => ..., poll => "[rw]+", cb => sub { my ($poll_got) = @_; ... }); - my $w = AnyEvent->io (after => $seconds, cb => sub { + +- only one io watcher per $fh and $poll type is allowed +(i.e. on a socket you can have one r + one w or one rw +watcher, not any more. + +- AnyEvent will keep filehandles alive, so as long as the watcher exists, +the filehandle exists. + + my $w = AnyEvent->timer (after => $seconds, cb => sub { ... }); - # watchers get canceled whenever $w is destroyed - # only one watcher per $fh and $poll type is allowed - # (i.e. on a socket you cna have one r + one w or one rw - # watcher, not any more. - # timers can only be used once +- io and time watchers get canceled whenever $w is destroyed, so keep a copy + +- timers can only be used once and must be recreated for repeated operation my $w = AnyEvent->condvar; # kind of main loop replacement - # can only be used once - $w->wait; # enters main loop till $condvar gets ->send - $w->broadcast; # wake up waiting and future wait's + $w->wait; # enters main loop till $condvar gets ->broadcast + $w->broadcast; # wake up current and all future wait's + +- condvars are used to give blocking behaviour when neccessary. Create +a condvar for any "request" or "event" your module might create, C<< +->broadcast >> it when the event happens and provide a function that calls +C<< ->wait >> for it. See the examples below. =head1 DESCRIPTION @@ -54,7 +64,7 @@ use strict 'vars'; use Carp; -our $VERSION = 0.2; +our $VERSION = 0.3; our $MODEL; our $AUTOLOAD; @@ -80,9 +90,7 @@ for (@models) { my ($model, $package) = @$_; if (scalar keys %{ *{"$package\::"} }) { - eval "require AnyEvent::Impl::$model" - or die; - + eval "require AnyEvent::Impl::$model"; last if $MODEL; } } @@ -92,9 +100,7 @@ for (@models) { my ($model, $package) = @$_; - eval "require AnyEvent::Impl::$model" - or die; - + eval "require AnyEvent::Impl::$model"; last if $MODEL; } @@ -141,13 +147,135 @@ $cv->wait; # wait until user enters /^q/i +=head1 REAL-WORLD EXAMPLE + +Consider the L module. It features (among others) the following +API calls, which are to freenet what HTTP GET requests are to http: + + my $data = $fcp->client_get ($url); # blocks + + my $transaction = $fcp->txn_client_get ($url); # does not block + $transaction->cb ( sub { ... } ); # set optional result callback + my $data = $transaction->result; # possibly blocks + +The C method works like C: it requests the +given URL and waits till the data has arrived. It is defined to be: + + sub client_get { $_[0]->txn_client_get ($_[1])->result } + +And in fact is automatically generated. This is the blocking API of +L, and it works as simple as in any other, similar, module. + +More complicated is C: It only creates a transaction +(completion, result, ...) object and initiates the transaction. + + my $txn = bless { }, Net::FCP::Txn::; + +It also creates a condition variable that is used to signal the completion +of the request: + + $txn->{finished} = AnyAvent->condvar; + +It then creates a socket in non-blocking mode. + + socket $txn->{fh}, ...; + fcntl $txn->{fh}, F_SETFL, O_NONBLOCK; + connect $txn->{fh}, ... + and !$!{EWOULDBLOCK} + and !$!{EINPROGRESS} + and Carp::croak "unable to connect: $!\n"; + +Then it creates a write-watcher which gets called whenever an error occurs +or the connection succeeds: + + $txn->{w} = AnyEvent->io (fh => $txn->{fh}, poll => 'w', cb => sub { $txn->fh_ready_w }); + +And returns this transaction object. The C callback gets +called as soon as the event loop detects that the socket is ready for +writing. + +The C method makes the socket blocking again, writes the +request data and replaces the watcher by a read watcher (waiting for reply +data). The actual code is more complicated, but that doesn't matter for +this example: + + fcntl $txn->{fh}, F_SETFL, 0; + syswrite $txn->{fh}, $txn->{request} + or die "connection or write error"; + $txn->{w} = AnyEvent->io (fh => $txn->{fh}, poll => 'r', cb => sub { $txn->fh_ready_r }); + +Again, C waits till all data has arrived, and then stores the +result and signals any possible waiters that the request ahs finished: + + sysread $txn->{fh}, $txn->{buf}, length $txn->{$buf}; + + if (end-of-file or data complete) { + $txn->{result} = $txn->{buf}; + $txn->{finished}->broadcast; + $txb->{cb}->($txn) of $txn->{cb}; # also call callback + } + +The C method, finally, just waits for the finished signal (if the +request was already finished, it doesn't wait, of course, and returns the +data: + + $txn->{finished}->wait; + return $txn->{result}; + +The actual code goes further and collects all errors (Cs, exceptions) +that occured during request processing. The C method detects +wether an exception as thrown (it is stored inside the $txn object) +and just throws the exception, which means connection errors and other +problems get reported tot he code that tries to use the result, not in a +random callback. + +All of this enables the following usage styles: + +1. Blocking: + + my $data = $fcp->client_get ($url); + +2. Blocking, but parallelizing: + + my @datas = map $_->result, + map $fcp->txn_client_get ($_), + @urls; + +Both blocking examples work without the module user having to know +anything about events. + +3a. Event-based in a main program, using any support Event module: + + use Event; + + $fcp->txn_client_get ($url)->cb (sub { + my $txn = shift; + my $data = $txn->result; + ... + }); + + Event::loop; + +3b. The module user could use AnyEvent, too: + + use AnyEvent; + + my $quit = AnyEvent->condvar; + + $fcp->txn_client_get ($url)->cb (sub { + ... + $quit->broadcast; + }); + + $quit->wait; + =head1 SEE ALSO -L, L, L, L, L, -L, -L, -L, -L. +Event modules: L, L, L, L, L. + +Implementations: L, L, L, L. + +Nontrivial usage example: L. =head1