File Coverage

blib/lib/IO/Async/Loop/AnyEvent.pm
Criterion Covered Total %
statement 13 13 100.0
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 18 100.0


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2011 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::Loop::AnyEvent;
7              
8 10     10   580968 use strict;
  10         22  
  10         382  
9 10     10   54 use warnings;
  10         22  
  10         497  
10              
11             our $VERSION = '0.03';
12 10     10   50 use constant API_VERSION => '0.33';
  10         23  
  10         643  
13              
14             # Force AnyEvent to detect a suitable model now, before we load
15             # IO::Async::Loop. Otherwise, AnyEvent will use AnyEvent::Impl::IOAsync
16             # which causes a circular dependency at runtime, leading to such problems as:
17             #
18             # Deep recursion on subroutine "AnyEvent::Impl::IOAsync::io" at ...
19              
20 10     10   9126 use AnyEvent;
  10         34663  
  10         390  
21 10     10   43 BEGIN { AnyEvent::detect() }
22              
23             use parent qw( IO::Async::Loop );
24              
25             use Carp;
26              
27             use constant ANYEVENT_6 => $AnyEvent::VERSION >= 6;
28              
29             =head1 NAME
30              
31             C - use C with C
32              
33             =head1 SYNOPSIS
34              
35             use IO::Async::Loop::AnyEvent;
36              
37             my $loop = IO::Async::Loop::AnyEvent->new();
38              
39             $loop->add( ... );
40              
41             $loop->add( IO::Async::Signal->new(
42             name => 'HUP',
43             on_receipt => sub { ... },
44             ) );
45              
46             $loop->loop_forever();
47              
48             =head1 DESCRIPTION
49              
50             This subclass of L uses L to perform its work.
51              
52             =head1 CONSTRUCTOR
53              
54             =cut
55              
56             =head2 $loop = IO::Async::Loop::AnyEvent->new
57              
58             This function returns a new instance of a C object.
59              
60             =cut
61              
62             sub new
63             {
64             my $class = shift;
65             my ( %args ) = @_;
66              
67             my $self = $class->SUPER::__new( %args );
68              
69             $self->{$_} = {} for qw( watch_r watch_w watch_time watch_signal watch_idle watch_child);
70              
71             return $self;
72             }
73              
74             sub loop_once
75             {
76             my $self = shift;
77             my ( $timeout ) = @_;
78              
79             my $cv = AnyEvent->condvar;
80             my $w;
81              
82             if( defined $timeout ) {
83             $w = AnyEvent->timer( after => $timeout, cb => sub { $cv->send } );
84             }
85              
86             if( ANYEVENT_6 ) {
87             # This method isn't technically documented by AnyEvent
88             AnyEvent->_poll;
89             }
90             else {
91             # This method isn't technically documented by AnyEvent
92             AnyEvent->one_event;
93             }
94             }
95              
96             sub loop_forever
97             {
98             my $self = shift;
99              
100             ( local $self->{loop_forever_cv} = AnyEvent->condvar )->recv;
101             }
102              
103             sub loop_stop
104             {
105             my $self = shift;
106              
107             $self->{loop_forever_cv}->send;
108             }
109              
110             sub watch_io
111             {
112             my $self = shift;
113             my %params = @_;
114              
115             my $handle = $params{handle} or die "Need a handle";
116              
117             if( my $on_read_ready = $params{on_read_ready} ) {
118             $self->{watch_r}{$handle} = AnyEvent->io(
119             fh => $handle,
120             poll => "r",
121             cb => $on_read_ready,
122             );
123             }
124              
125             if( my $on_write_ready = $params{on_write_ready} ) {
126             $self->{watch_w}{$handle} = AnyEvent->io(
127             fh => $handle,
128             poll => "w",
129             cb => $on_write_ready,
130             );
131             }
132             }
133              
134             sub unwatch_io
135             {
136             my $self = shift;
137             my %params = @_;
138              
139             my $handle = $params{handle} or die "Need a handle";
140              
141             if( $params{on_read_ready} ) {
142             delete $self->{watch_r}{$handle};
143             }
144              
145             if( $params{on_write_ready} ) {
146             delete $self->{watch_w}{$handle};
147             }
148             }
149              
150             sub enqueue_timer
151             {
152             my $self = shift;
153             my %params = @_;
154              
155             my $now = $self->time;
156             my $delay = $self->_build_time( %params, now => $now ) - $now;
157              
158             my $code = $params{code} or croak "Expected 'code' as CODE ref";
159              
160             my $w = AnyEvent->timer( after => $delay, cb => $code );
161              
162             $self->{watch_time}{$w} = [ $w, $code ];
163             return $w;
164             }
165              
166             sub cancel_timer
167             {
168             my $self = shift;
169             my ( $id ) = @_;
170              
171             delete $self->{watch_time}{$id};
172             }
173              
174             sub requeue_timer
175             {
176             my $self = shift;
177             my ( $id, %params ) = @_;
178              
179             my $code = ( delete $self->{watch_time}{$id} )->[1];
180             return $self->enqueue_timer( %params, code => $code );
181             }
182              
183             sub watch_signal
184             {
185             my $self = shift;
186             my ( $signal, $code ) = @_;
187              
188             $self->{watch_signal}{$signal} = AnyEvent->signal(
189             signal => $signal,
190             cb => $code,
191             );
192             }
193              
194             sub unwatch_signal
195             {
196             my $self = shift;
197             my ( $signal ) = @_;
198              
199             delete $self->{watch_signal}{$signal};
200             }
201              
202             sub watch_idle
203             {
204             my $self = shift;
205             my %params = @_;
206              
207             my $when = delete $params{when} or croak "Expected 'when'";
208              
209             my $code = delete $params{code} or croak "Expected 'code' as a CODE ref";
210              
211             $when eq "later" or croak "Expected 'when' to be 'later'";
212              
213             my $key;
214             my $w = AnyEvent->timer(
215             after => 0,
216             cb => sub {
217             delete $self->{watch_idle}{$key};
218             goto &$code;
219             },
220             );
221              
222             $key = "$w";
223             $self->{watch_idle}{$key} = $w;
224             return $key;
225             }
226              
227             sub unwatch_idle
228             {
229             my $self = shift;
230             my ( $id ) = @_;
231              
232             delete $self->{watch_idle}{$id};
233             }
234              
235             sub watch_child
236             {
237             my $self = shift;
238             my ( $pid, $code ) = @_;
239              
240             $self->{watch_child}{$pid} = AnyEvent->child( pid => $pid, cb => $code );
241             }
242              
243             sub unwatch_child
244             {
245             my $self = shift;
246             my ( $pid ) = @_;
247              
248             delete $self->{watch_child}{$pid};
249             }
250              
251             =head1 BUGS
252              
253             =over 4
254              
255             =item *
256              
257             C and C don't work properly against
258             C. At least, the unit tests fail, and some scheduled
259             CODErefs never get executed, and sit in the internal queue of the inner-nested
260             C that C itself constructed. An easy
261             workaround here is simply to pick another AnyEvent model, by using the
262             C environment variable.
263              
264             That all said, I am honestly surprised this is the only thing that breaks,
265             when C is nested upon C itself running atop another
266             C.
267              
268             =item *
269              
270             The implementation of the C method requires the use of an
271             undocumented C method (C before version 6, C<_poll>
272             thereafter). This happens to work at the time of writing, but as it is
273             undocumented it may be subject to change.
274              
275             The C method does not rely on this undocumented method, so
276             should be safe from upstream changes. Furthremore, if C rather than
277             C remains ultimately in control of the runtime, by waiting on
278             condvars, this should not be problematic.
279              
280             =back
281              
282             =cut
283              
284             =head1 AUTHOR
285              
286             Paul Evans
287              
288             =cut
289              
290             0x55AA;