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   73581 use strict;
  15         41  
  15         483  
9 15     15   82 use warnings;
  15         32  
  15         663  
10              
11             our $VERSION = '0.79';
12 15     15   99 use constant API_VERSION => '0.49';
  15         28  
  15         1187  
13              
14 15     15   603 use base qw( IO::Async::Loop );
  15         59  
  15         11287  
15              
16 15     15   148 use IO::Async::OS;
  15         33  
  15         334  
17              
18 15     15   76 use Carp;
  15         29  
  15         1117  
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   151 use constant FAKE_ISREG_READY => IO::Async::OS->HAVE_FAKE_ISREG_READY;
  15         31  
  15         1030  
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   94 use constant SELECT_CONNECT_EVEC => IO::Async::OS->HAVE_SELECT_CONNECT_EVEC;
  15         24  
  15         924  
26              
27 15     15   89 use constant _CAN_WATCHDOG => 1;
  15         37  
  15         718  
28 15     15   88 use constant WATCHDOG_ENABLE => IO::Async::Loop->WATCHDOG_ENABLE;
  15         25  
  15         14395  
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 116 my $class = shift;
86              
87 14         105 my $self = $class->__new( @_ );
88              
89 14         31 $self->{rvec} = '';
90 14         30 $self->{wvec} = '';
91 14         28 $self->{evec} = '';
92              
93 14         23 $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 88     88 1 293 my $self = shift;
132 88         226 my ( $readref, $writeref, $exceptref, $timeref ) = @_;
133              
134             # BITWISE operations
135 88         339 $$readref |= $self->{rvec};
136 88         240 $$writeref |= $self->{wvec};
137 88         200 $$exceptref |= $self->{evec};
138              
139 88         532 $self->_adjust_timeout( $timeref );
140              
141 88         151 $$timeref = 0 if FAKE_ISREG_READY and length $self->{avec};
142              
143             # Round up to nearest millisecond
144 88 100       229 if( $$timeref ) {
145 76         249 my $mils = $$timeref * 1000;
146 76         246 my $fraction = $mils - int $mils;
147 76 100       226 $$timeref += ( 1 - $fraction ) / 1000 if $fraction;
148             }
149              
150 88         184 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 86     86 1 3512021 my $self = shift;
177 86         314 my ( $readvec, $writevec, $exceptvec ) = @_;
178              
179 86         241 my $iowatches = $self->{iowatches};
180              
181 86         173 my $count = 0;
182              
183 86         170 alarm( IO::Async::Loop->WATCHDOG_INTERVAL ) if WATCHDOG_ENABLE;
184              
185 86         516 foreach my $fd ( keys %$iowatches ) {
186 63 100       259 my $watch = $iowatches->{$fd} or next;
187              
188 62         504 my $fileno = $watch->[0]->fileno;
189              
190 62 100 100     869 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       267 $count++, $watch->[1]->() if defined $watch->[1];
193             }
194              
195 62 50 100     797 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       27 $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 86         533 $self->_manage_queues;
206              
207 86         327 alarm( 0 ) if WATCHDOG_ENABLE;
208             }
209              
210             sub is_running
211             {
212 1     1 0 5 my $self = shift;
213 1         10 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 82     82 1 222 my $self = shift;
231 82         212 my ( $timeout ) = @_;
232              
233 82         477 my ( $rvec, $wvec, $evec ) = ('') x 3;
234              
235 82         450 $self->pre_select( \$rvec, \$wvec, \$evec, \$timeout );
236              
237 82         657 $self->pre_wait;
238 82         22405133 my $ret = select( $rvec, $wvec, $evec, $timeout );
239 82         1744 $self->post_wait;
240              
241 82 100       2067 if( $ret < 0 ) {
242             # r/w/e vec can't be trusted
243 17         59 $rvec = $wvec = $evec = '';
244             }
245              
246             {
247 82         195 local $!;
  82         960  
248 82         472 $self->post_select( $rvec, $wvec, $evec );
249             }
250              
251 82         432 return $ret;
252             }
253              
254             sub watch_io
255             {
256 18     18 1 2249 my $self = shift;
257 18         76 my %params = @_;
258              
259 18         185 $self->__watch_io( %params );
260              
261 18         65 my $fileno = $params{handle}->fileno;
262              
263 18 100       196 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         35 vec( $self->{evec}, $fileno, 1 ) = 1 if SELECT_CONNECT_EVEC and $params{on_write_ready};
269              
270 18         68 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 1086 my $self = shift;
276 12         50 my %params = @_;
277              
278 12         82 $self->__unwatch_io( %params );
279              
280 12         41 my $fileno = $params{handle}->fileno;
281              
282 12 100       106 vec( $self->{rvec}, $fileno, 1 ) = 0 if $params{on_read_ready};
283 12 100       49 vec( $self->{wvec}, $fileno, 1 ) = 0 if $params{on_write_ready};
284              
285 12         23 vec( $self->{evec}, $fileno, 1 ) = 0 if SELECT_CONNECT_EVEC and $params{on_write_ready};
286              
287 12         22 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         599 $_ =~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;