--- AnyEvent/lib/AnyEvent.pm 2005/12/01 22:04:50 1.4 +++ AnyEvent/lib/AnyEvent.pm 2005/12/30 01:28:31 1.7 @@ -6,26 +6,42 @@ =head1 SYNOPSIS -use AnyEvent; + 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 (limitation by +Tk). + +* the C<$poll_got> passed to the handler needs to be checked by looking +for single characters (e.g. with a regex), as it can contain more event +types than were requested (e.g. a 'w' watcher might generate 'rw' events, +limitation by Glib). + +* 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 (limitation by Glib and Tk). 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,12 +70,14 @@ use strict 'vars'; use Carp; -our $VERSION = 0.2; +our $VERSION = '0.4'; our $MODEL; our $AUTOLOAD; our @ISA; +our $verbose = $ENV{PERL_ANYEVENT_VERBOSE}*1; + my @models = ( [Coro => Coro::Event::], [Event => Event::], @@ -79,8 +97,9 @@ # check for already loaded models for (@models) { my ($model, $package) = @$_; - if (scalar keys %{ *{"$package\::"} }) { + if (${"$package\::VERSION"} > 0) { eval "require AnyEvent::Impl::$model"; + warn "AnyEvent: found model '$model', using it.\n" if $MODEL && $verbose > 1; last if $MODEL; } } @@ -91,6 +110,7 @@ for (@models) { my ($model, $package) = @$_; eval "require AnyEvent::Impl::$model"; + warn "AnyEvent: autprobed and loaded model '$model', using it.\n" if $MODEL && $verbose > 1; last if $MODEL; } @@ -107,6 +127,13 @@ =back +=head1 ENVIRONMENT VARIABLES + +The following environment variables are used by this module: + +C when set to C<2> or higher, reports which event +model gets used. + =head1 EXAMPLE The following program uses an io watcher to read data from stdin, a timer @@ -137,13 +164,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