File Coverage

blib/lib/IO/Async/Loop/Select.pm
Criterion Covered Total %
statement 95 95 100.0
branch 22 24 91.6
condition 6 6 100.0
subroutine 17 17 100.0
pod 6 7 85.7
total 146 149 97.9


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, 2007-2020 -- leonerd@leonerd.org.uk
5              
6             package IO::Async::Loop::Select;
7              
8 15     15   72872 use strict;
  15         49  
  15         481  
9 15     15   78 use warnings;
  15         30  
  15         674  
10              
11             our $VERSION = '0.801';
12 15     15   83 use constant API_VERSION => '0.49';
  15         30  
  15         1438  
13              
14 15     15   270 use base qw( IO::Async::Loop );
  15         41  
  15         10887  
15              
16 15     15   125 use IO::Async::OS;
  15         32  
  15         404  
17              
18 15     15   88 use Carp;
  15         32  
  15         1030  
19              
20             # select() on most platforms claims that ISREG files are always read- and
21             # write-ready, but not on MSWin32. We need to fake this
22 15     15   111 use constant FAKE_ISREG_READY => IO::Async::OS->HAVE_FAKE_ISREG_READY;
  15         34  
  15         1012  
23             # select() on most platforms indicates write-ready when connect() fails, but
24             # not on MSWin32. Have to pull from evec in that case
25 15     15   92 use constant SELECT_CONNECT_EVEC => IO::Async::OS->HAVE_SELECT_CONNECT_EVEC;
  15         33  
  15         800  
26              
27 15     15   90 use constant _CAN_WATCHDOG => 1;
  15         23  
  15         736  
28 15     15   92 use constant WATCHDOG_ENABLE => IO::Async::Loop->WATCHDOG_ENABLE;
  15         32  
  15         14028  
29              
30             =head1 NAME
31              
32             C - use L with C
33              
34             =head1 SYNOPSIS
35              
36             Normally an instance of this class would not be directly constructed by a
37             program. It may however, be useful for runinng L with an existing
38             program already using a C
39              
40             use IO::Async::Loop::Select;
41              
42             my $loop = IO::Async::Loop::Select->new;
43              
44             $loop->add( ... );
45              
46             while(1) {
47             my ( $rvec, $wvec, $evec ) = ('') x 3;
48             my $timeout;
49              
50             $loop->pre_select( \$rvec, \$wvec, \$evec, \$timeout );
51             ...
52             my $ret = select( $rvec, $wvec, $evec, $timeout );
53             ...
54             $loop->post_select( $rvec, $evec, $wvec );
55             }
56              
57             =head1 DESCRIPTION
58              
59             This subclass of L uses the C syscall to perform
60             read-ready and write-ready tests.
61              
62             To integrate with an existing C
63             C and C can be called immediately before and
64             after a C
65             exceptional-state bitvectors are set by the C method, and tested
66             by the C method to pick which event callbacks to invoke.
67              
68             =cut
69              
70             =head1 CONSTRUCTOR
71              
72             =cut
73              
74             =head2 new
75              
76             $loop = IO::Async::Loop::Select->new
77              
78             This function returns a new instance of a C object.
79             It takes no special arguments.
80              
81             =cut
82              
83             sub new
84             {
85 14     14 1 124 my $class = shift;
86              
87 14         110 my $self = $class->__new( @_ );
88              
89 14         33 $self->{rvec} = '';
90 14         28 $self->{wvec} = '';
91 14         28 $self->{evec} = '';
92              
93 14         41 $self->{avec} = ''; # Bitvector of handles always to claim are ready
94              
95 14         47 return $self;
96             }
97              
98             =head1 METHODS
99              
100             =cut
101              
102             =head2 pre_select
103              
104             $loop->pre_select( \$readvec, \$writevec, \$exceptvec, \$timeout )
105              
106             This method prepares the bitvectors for a C
107             that the Loop is interested in. It will also adjust the C<$timeout> value if
108             appropriate, reducing it if the next event timeout the Loop requires is sooner
109             than the current value.
110              
111             =over 8
112              
113             =item \$readvec
114              
115             =item \$writevec
116              
117             =item \$exceptvec
118              
119             Scalar references to the reading, writing and exception bitvectors
120              
121             =item \$timeout
122              
123             Scalar reference to the timeout value
124              
125             =back
126              
127             =cut
128              
129             sub pre_select
130             {
131 83     83 1 328 my $self = shift;
132 83         222 my ( $readref, $writeref, $exceptref, $timeref ) = @_;
133              
134             # BITWISE operations
135 83         420 $$readref |= $self->{rvec};
136 83         236 $$writeref |= $self->{wvec};
137 83         195 $$exceptref |= $self->{evec};
138              
139 83         653 $self->_adjust_timeout( $timeref );
140              
141 83         150 $$timeref = 0 if FAKE_ISREG_READY and length $self->{avec};
142              
143             # Round up to nearest millisecond
144 83 100       269 if( $$timeref ) {
145 71         231 my $mils = $$timeref * 1000;
146 71         198 my $fraction = $mils - int $mils;
147 71 100       245 $$timeref += ( 1 - $fraction ) / 1000 if $fraction;
148             }
149              
150 83         200 return;
151             }
152              
153             =head2 post_select
154              
155             $loop->post_select( $readvec, $writevec, $exceptvec )
156              
157             This method checks the returned bitvectors from a C
158             any of the callbacks that are appropriate.
159              
160             =over 8
161              
162             =item $readvec
163              
164             =item $writevec
165              
166             =item $exceptvec
167              
168             Scalars containing the read-ready, write-ready and exception bitvectors
169              
170             =back
171              
172             =cut
173              
174             sub post_select
175             {
176 81     81 1 3511176 my $self = shift;
177 81         354 my ( $readvec, $writevec, $exceptvec ) = @_;
178              
179 81         241 my $iowatches = $self->{iowatches};
180              
181 81         171 my $count = 0;
182              
183 81         147 alarm( IO::Async::Loop->WATCHDOG_INTERVAL ) if WATCHDOG_ENABLE;
184              
185 81         494 foreach my $fd ( keys %$iowatches ) {
186 58 100       248 my $watch = $iowatches->{$fd} or next;
187              
188 57         717 my $fileno = $watch->[0]->fileno;
189              
190 57 100 100     851 if( vec( $readvec, $fileno, 1 ) or
191             FAKE_ISREG_READY and vec( $self->{avec}, $fileno, 1 ) and vec( $self->{rvec}, $fileno, 1 ) ) {
192 30 50       240 $count++, $watch->[1]->() if defined $watch->[1];
193             }
194              
195 57 50 100     650 if( vec( $writevec, $fileno, 1 ) or
      100        
196             SELECT_CONNECT_EVEC and vec( $exceptvec, $fileno, 1 ) or
197             FAKE_ISREG_READY and vec( $self->{avec}, $fileno, 1 ) and vec( $self->{wvec}, $fileno, 1 ) ) {
198 5 100       23 $count++, $watch->[2]->() if defined $watch->[2];
199             }
200             }
201              
202             # Since we have no way to know if the timeout occurred, we'll have to
203             # attempt to fire any waiting timeout events anyway
204              
205 81         483 $self->_manage_queues;
206              
207 81         397 alarm( 0 ) if WATCHDOG_ENABLE;
208             }
209              
210             sub is_running
211             {
212 1     1 0 3 my $self = shift;
213 1         9 return $self->{running};
214             }
215              
216             =head2 loop_once
217              
218             $count = $loop->loop_once( $timeout )
219              
220             This method calls the C method to prepare the bitvectors for a
221             C
222             result. It returns the total number of callbacks invoked by the
223             C method, or C if the underlying C syscall
224             returned an error.
225              
226             =cut
227              
228             sub loop_once
229             {
230 77     77 1 203 my $self = shift;
231 77         373 my ( $timeout ) = @_;
232              
233 77         383 my ( $rvec, $wvec, $evec ) = ('') x 3;
234              
235 77         639 $self->pre_select( \$rvec, \$wvec, \$evec, \$timeout );
236              
237 77         462 $self->pre_wait;
238 77         22710764 my $ret = select( $rvec, $wvec, $evec, $timeout );
239 77         2177 $self->post_wait;
240              
241 77 100       2312 if( $ret < 0 ) {
242             # r/w/e vec can't be trusted
243 12         56 $rvec = $wvec = $evec = '';
244             }
245              
246             {
247 77         238 local $!;
  77         1259  
248 77         470 $self->post_select( $rvec, $wvec, $evec );
249             }
250              
251 77         364 return $ret;
252             }
253              
254             sub watch_io
255             {
256 18     18 1 2911 my $self = shift;
257 18         83 my %params = @_;
258              
259 18         180 $self->__watch_io( %params );
260              
261 18         103 my $fileno = $params{handle}->fileno;
262              
263 18 100       185 vec( $self->{rvec}, $fileno, 1 ) = 1 if $params{on_read_ready};
264 18 100       83 vec( $self->{wvec}, $fileno, 1 ) = 1 if $params{on_write_ready};
265              
266             # MSWin32 does not indicate writeready for connect() errors, HUPs, etc
267             # but it does indicate exceptional
268 18         36 vec( $self->{evec}, $fileno, 1 ) = 1 if SELECT_CONNECT_EVEC and $params{on_write_ready};
269              
270 18         62 vec( $self->{avec}, $fileno, 1 ) = 1 if FAKE_ISREG_READY and stat( $params{handle} ) and -f _;
271             }
272              
273             sub unwatch_io
274             {
275 12     12 1 1115 my $self = shift;
276 12         61 my %params = @_;
277              
278 12         73 $self->__unwatch_io( %params );
279              
280 12         42 my $fileno = $params{handle}->fileno;
281              
282 12 100       115 vec( $self->{rvec}, $fileno, 1 ) = 0 if $params{on_read_ready};
283 12 100       48 vec( $self->{wvec}, $fileno, 1 ) = 0 if $params{on_write_ready};
284              
285 12         22 vec( $self->{evec}, $fileno, 1 ) = 0 if SELECT_CONNECT_EVEC and $params{on_write_ready};
286              
287 12         21 vec( $self->{avec}, $fileno, 1 ) = 0 if FAKE_ISREG_READY and stat( $params{handle} ) and -f _;
288              
289             # vec will grow a bit vector as needed, but never shrink it. We'll trim
290             # trailing null bytes
291 12         299 $_ =~s/\0+\z// for $self->{rvec}, $self->{wvec}, $self->{evec}, $self->{avec};
292             }
293              
294             =head1 SEE ALSO
295              
296             =over 4
297              
298             =item *
299              
300             L - OO interface to select system call
301              
302             =back
303              
304             =head1 AUTHOR
305              
306             Paul Evans
307              
308             =cut
309              
310             0x55AA;