File Coverage

blib/lib/Mojo/Reactor/EV.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Mojo::Reactor::EV;
2 8     8   70 use Mojo::Base 'Mojo::Reactor::Poll';
  8         26  
  8         55  
3              
4 8     8   67 use Carp qw(croak);
  8         36  
  8         515  
5 8     8   1724 use EV 4.32;
  0            
  0            
6              
7             my $EV;
8              
9             sub DESTROY { undef $EV }
10              
11             sub again {
12             my ($self, $id, $after) = @_;
13             croak 'Timer not active' unless my $timer = $self->{timers}{$id};
14             my $w = $timer->{watcher};
15             defined $after ? $w->set($after, $w->repeat ? $after : 0) : $w->again;
16             }
17              
18             # We have to fall back to Mojo::Reactor::Poll, since EV is unique
19             sub new { $EV++ ? Mojo::Reactor::Poll->new : shift->SUPER::new }
20              
21             sub one_tick {
22             my $self = shift;
23             local $self->{running} = 1 unless $self->{running};
24             EV::run(EV::RUN_ONCE);
25             }
26              
27             sub recurring { shift->_timer(1, @_) }
28              
29             sub start {
30             my $self = shift;
31             local $self->{running} = 1 unless $self->{running};
32             EV::run;
33             }
34              
35             sub stop { EV::break(EV::BREAK_ALL) }
36              
37             sub timer { shift->_timer(0, @_) }
38              
39             sub watch {
40             my ($self, $handle, $read, $write) = @_;
41              
42             my $fd = fileno $handle;
43             croak 'I/O watcher not active' unless my $io = $self->{io}{$fd};
44              
45             my $mode = 0;
46             $mode |= EV::READ if $read;
47             $mode |= EV::WRITE if $write;
48              
49             if ($mode == 0) { delete $io->{watcher} }
50             elsif (my $w = $io->{watcher}) { $w->events($mode) }
51             else {
52             my $cb = sub {
53             my ($w, $revents) = @_;
54             $self->_try('I/O watcher', $self->{io}{$fd}{cb}, 0) if EV::READ & $revents;
55             $self->_try('I/O watcher', $self->{io}{$fd}{cb}, 1) if EV::WRITE & $revents && $self->{io}{$fd};
56             };
57             $io->{watcher} = EV::io($fd, $mode, $cb);
58             }
59              
60             return $self;
61             }
62              
63             sub _timer {
64             my ($self, $recurring, $after, $cb) = @_;
65             $after ||= 0.0001 if $recurring;
66              
67             my $id = $self->_id;
68             my $wrapper = sub {
69             delete $self->{timers}{$id} unless $recurring;
70             $self->_try('Timer', $cb);
71             };
72             EV::now_update() if $after > 0;
73             $self->{timers}{$id}{watcher} = EV::timer($after, $after, $wrapper);
74              
75             return $id;
76             }
77              
78             1;
79              
80             =encoding utf8
81              
82             =head1 NAME
83              
84             Mojo::Reactor::EV - Low-level event reactor with libev support
85              
86             =head1 SYNOPSIS
87              
88             use Mojo::Reactor::EV;
89              
90             # Watch if handle becomes readable or writable
91             my $reactor = Mojo::Reactor::EV->new;
92             $reactor->io($first => sub ($reactor, $writable) {
93             say $writable ? 'First handle is writable' : 'First handle is readable';
94             });
95              
96             # Change to watching only if handle becomes writable
97             $reactor->watch($first, 0, 1);
98              
99             # Turn file descriptor into handle and watch if it becomes readable
100             my $second = IO::Handle->new_from_fd($fd, 'r');
101             $reactor->io($second => sub ($reactor, $writable) {
102             say $writable ? 'Second handle is writable' : 'Second handle is readable';
103             })->watch($second, 1, 0);
104              
105             # Add a timer
106             $reactor->timer(15 => sub ($reactor) {
107             $reactor->remove($first);
108             $reactor->remove($second);
109             say 'Timeout!';
110             });
111              
112             # Start reactor if necessary
113             $reactor->start unless $reactor->is_running;
114              
115             =head1 DESCRIPTION
116              
117             L is a low-level event reactor based on L (4.32+).
118              
119             =head1 EVENTS
120              
121             L inherits all events from L.
122              
123             =head1 METHODS
124              
125             L inherits all methods from L and implements the following new ones.
126              
127             =head2 again
128              
129             $reactor->again($id);
130             $reactor->again($id, 0.5);
131              
132             Restart timer and optionally change the invocation time. Note that this method requires an active timer.
133              
134             =head2 new
135              
136             my $reactor = Mojo::Reactor::EV->new;
137              
138             Construct a new L object.
139              
140             =head2 one_tick
141              
142             $reactor->one_tick;
143              
144             Run reactor until an event occurs or no events are being watched anymore.
145              
146             # Don't block longer than 0.5 seconds
147             my $id = $reactor->timer(0.5 => sub {});
148             $reactor->one_tick;
149             $reactor->remove($id);
150              
151             =head2 recurring
152              
153             my $id = $reactor->recurring(0.25 => sub {...});
154              
155             Create a new recurring timer, invoking the callback repeatedly after a given amount of time in seconds.
156              
157             =head2 start
158              
159             $reactor->start;
160              
161             Start watching for I/O and timer events, this will block until L is called or no events are being watched
162             anymore.
163              
164             # Start reactor only if it is not running already
165             $reactor->start unless $reactor->is_running;
166              
167             =head2 stop
168              
169             $reactor->stop;
170              
171             Stop watching for I/O and timer events.
172              
173             =head2 timer
174              
175             my $id = $reactor->timer(0.5 => sub {...});
176              
177             Create a new timer, invoking the callback after a given amount of time in seconds.
178              
179             =head2 watch
180              
181             $reactor = $reactor->watch($handle, $readable, $writable);
182              
183             Change I/O events to watch handle for with true and false values. Note that this method requires an active I/O watcher.
184              
185             # Watch only for readable events
186             $reactor->watch($handle, 1, 0);
187              
188             # Watch only for writable events
189             $reactor->watch($handle, 0, 1);
190              
191             # Watch for readable and writable events
192             $reactor->watch($handle, 1, 1);
193              
194             # Pause watching for events
195             $reactor->watch($handle, 0, 0);
196              
197             =head1 SEE ALSO
198              
199             L, L, L.
200              
201             =cut