File Coverage

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