| 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 | 65 | use Mojo::Base 'Mojo::Reactor::Poll'; | |||
| 8 | 24 | ||||||
| 8 | 55 | ||||||
| 3 | |||||||
| 4 | 8 | 8 | 65 | use Carp qw(croak); | |||
| 8 | 32 | ||||||
| 8 | 522 | ||||||
| 5 | 8 | 8 | 1651 | 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 |
||||||
| 118 | |||||||
| 119 | =head1 EVENTS | ||||||
| 120 | |||||||
| 121 | L |
||||||
| 122 | |||||||
| 123 | =head1 METHODS | ||||||
| 124 | |||||||
| 125 | L |
||||||
| 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 |
||||||
| 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"stop"> 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 |
||||||
| 200 | |||||||
| 201 | =cut |