File Coverage

blib/lib/IO/Lambda/Loop/Select.pm
Criterion Covered Total %
statement 135 148 91.2
branch 52 74 70.2
condition 17 24 70.8
subroutine 13 13 100.0
pod 7 8 87.5
total 224 267 83.9


line stmt bran cond sub pod time code
1             # $Id: Select.pm,v 1.18 2010/01/01 14:52:17 dk Exp $
2              
3             package IO::Lambda::Loop::Select;
4 27     27   144 use strict;
  27         53  
  27         720  
5 27     27   140 use warnings;
  27         53  
  27         970  
6 27     27   141 use Errno qw(EINTR EAGAIN);
  27         41  
  27         4091  
7 27     27   152 use IO::Lambda qw(:constants);
  27         61  
  27         5251  
8 27     27   147 use Time::HiRes qw(time);
  27         49  
  27         233  
9              
10             IO::Lambda::Loop::default('Select');
11              
12             our $DEBUG = $IO::Lambda::DEBUG{select} || 0;
13              
14             # IO::Select::select doesn't distinguish between select returning 0 and -1, don't have
15             # time to fix that. I'll just use a plain select instead, it'll be faster also.
16              
17             sub new
18             {
19 27     27 1 97 my $self = bless {} , shift;
20 27         225 $self-> {$_} = '' for qw(read write exc);
21 27         73 $self-> {items} = {};
22 27         63 $self-> {timers} = [];
23 27         121 return $self;
24             }
25              
26             sub empty
27             {
28 485184     485184 1 607838 my $self = shift;
29             return (
30 485184         820131 @{$self->{timers}} +
31 485184 100       506489 keys(%{$self-> {items}})
  485184         1773243  
32             ) ? 0 : 1;
33             }
34              
35             sub yield
36             {
37 218     218 1 574 my ( $self, $nonblocking ) = @_;
38              
39 218 50       629 return if $self-> empty;
40              
41 218         396 my $t;
42 218 50       548 $t = 0 if $nonblocking;
43              
44 218         400 my ($min,$max) = ( 0, -1);
45 218         762 my $ct = time;
46              
47             # timers
48 218         331 for ( @{$self-> {timers}}) {
  218         716  
49 136 100 100     1235 $t = $_->[WATCH_DEADLINE]
      33        
50             if defined $_->[WATCH_DEADLINE] and
51             (!defined($t) or $t > $_-> [WATCH_DEADLINE]);
52             }
53              
54             # handles
55 218         429 my ( $R, $W, $E) = @{$self}{qw(read write exc)};
  218         932  
56              
57 218         396 while ( my ( $fileno, $bucket) = each %{ $self-> {items}} ) {
  469         1954  
58 251         534 for ( @$bucket) {
59 251 100 100     999 $t = $_->[WATCH_DEADLINE]
      66        
60             if defined $_->[WATCH_DEADLINE] and
61             (!defined($t) or $t > $_-> [WATCH_DEADLINE]);
62             }
63 251 50       727 warn "select: fileno $fileno\n" if $DEBUG;
64 251 100       846 $max = $fileno if $max < $fileno;
65 251 50 33     1904 $min = $fileno if !defined($min) or $min > $fileno;
66             }
67 218 100       784 if ( defined $t) {
    50          
68 110         228 $t -= $ct;
69 110 100       375 $t = 0 if $t < 0;
70 110 50       342 warn "select: timeout=$t\n" if $DEBUG;
71             } elsif ( $DEBUG) {
72 0         0 warn "select: no timeout\n";
73             }
74              
75             # do select
76 218         5743407 my $n = select( $R, $W, $E, $t);
77 218 50       1020 warn "select: $n handles ready\n" if $DEBUG;
78 218 100       709 if ( $n < 0) {
79 1 50 33     35 if ( $! == EINTR or $! == EAGAIN) {
80             # ignore
81 1 50       8 warn "select: $!\n" if $DEBUG;
82             } else {
83             # find out the rogue handles
84 0 0       0 if ( $DEBUG > 1) {
85 0         0 my $h = $R | $W | $E;
86 0         0 for ( my $i = 0; $i < length($h); $i++) {
87 0         0 my $v = '';
88 0         0 for ( my $j = 0; $j < 8; $j++) {
89 0         0 my $fd = $i * 8 + $j;
90 0 0       0 next unless vec($h,$fd,1);
91 0         0 vec($v,$fd,1) = 1;
92 0 0       0 next if select($v,$v,$v,0) >= 0;
93 0         0 warn "select: bad handle #$fd\n";
94             }
95             }
96             }
97 0         0 die "select() error:$!:$^E";
98             }
99             }
100            
101             # expired timers
102 218         387 my ( @kill, @expired);
103              
104 218         712 $t = $self-> {timers};
105             @$t = grep {
106 218 100       673 ($$_[WATCH_DEADLINE] <= $ct) ? do {
  136         896  
107 47         153 push @expired, $_;
108 47         178 0;
109             } : 1;
110             } @$t;
111              
112             # handles
113 218 100       697 if ( $n > 0) {
114             # process selected handles
115 133   100     1236 for ( my $i = $min; $i <= $max && $n > 0; $i++) {
116 1393         3462 my $what =
117             vec( $R, $i, 1) * IO_READ +
118             vec( $W, $i, 1) * IO_WRITE +
119             vec( $E, $i, 1) * IO_EXCEPTION
120             ;
121 1393 100       8104 next unless $what;
122              
123 177         474 my $bucket = $self-> {items}-> {$i};
124             @$bucket = grep {
125 177 50       362 ($$_[WATCH_IO_FLAGS] & $what) ? do {
  177         485  
126 177         307 $$_[WATCH_IO_FLAGS] &= $what;
127 177         341 push @expired, $_;
128 177         637 0;
129             } : 1;
130             } @$bucket;
131 177 50       1014 delete $self-> {items}->{$i} unless @$bucket;
132 177         1048 $n--;
133             }
134             } else {
135             # else process timeouts
136 85         179 my @kill;
137 85         190 while ( my ( $fileno, $bucket) = each %{ $self-> {items}}) {
  93         568  
138             @$bucket = grep {
139 8         22 (
140             defined($_->[WATCH_DEADLINE]) &&
141             $_->[WATCH_DEADLINE] <= $ct
142 8 100 100     63 ) ? do {
143 1         3 $$_[WATCH_IO_FLAGS] = 0;
144 1         2 push @expired, $_;
145 1         3 0;
146             } : 1;
147             } @$bucket;
148 8 100       50 push @kill, $fileno unless @$bucket;
149             }
150 85         186 delete @{$self->{items}}{@kill};
  85         265  
151             }
152 218         994 $self-> rebuild_vectors;
153            
154             # call them
155 218         1662 $$_[WATCH_OBJ]-> io_handler( $_) for @expired;
156             }
157              
158             sub watch
159             {
160 183     183 1 361 my ( $self, $rec) = @_;
161 183         438 my $fileno = fileno $rec->[WATCH_IO_HANDLE];
162 183 50       489 die "Invalid filehandle" unless defined $fileno;
163 183         309 my $flags = $rec->[WATCH_IO_FLAGS];
164              
165 183 100       1407 vec($self-> {read}, $fileno, 1) = 1 if $flags & IO_READ;
166 183 100       724 vec($self-> {write}, $fileno, 1) = 1 if $flags & IO_WRITE;
167 183 100       631 vec($self-> {exc}, $fileno, 1) = 1 if $flags & IO_EXCEPTION;
168              
169 183         278 push @{$self-> {items}-> {$fileno}}, $rec;
  183         1222  
170             }
171              
172             sub after
173             {
174 58     58 1 169 my ( $self, $rec) = @_;
175 58         91 push @{$self-> {timers}}, $rec;
  58         227  
176             }
177              
178             sub remove
179             {
180 46     46 1 173 my ($self, $obj) = @_;
181              
182 46         161 @{$self-> {timers}} = grep {
183 7 50       90 defined($_->[WATCH_OBJ]) and $_->[WATCH_OBJ] != $obj
184 46         136 } @{$self-> {timers}};
  46         158  
185              
186 46         114 my @kill;
187 46         117 while ( my ( $fileno, $bucket) = each %{$self->{items}}) {
  71         543  
188 25 50       76 @$bucket = grep { defined($_->[WATCH_OBJ]) and $_->[WATCH_OBJ] != $obj } @$bucket;
  25         350  
189 25 100       160 next if @$bucket;
190 5         27 push @kill, $fileno;
191             }
192 46         156 delete @{$self->{items}}{@kill};
  46         167  
193              
194 46         194 $self-> rebuild_vectors;
195             }
196              
197             sub remove_event
198             {
199 10     10 1 30 my ($self, $rec) = @_;
200            
201 10         22 @{$self-> {timers}} = grep { $_ != $rec } @{$self-> {timers}};
  10         41  
  7         36  
  10         164  
202              
203 10         36 my @kill;
204 10         28 while ( my ( $fileno, $bucket) = each %{$self->{items}}) {
  12         67  
205 2         6 @$bucket = grep { $_ != $rec } @$bucket;
  2         11  
206 2 50       8 next if @$bucket;
207 0         0 push @kill, $fileno;
208             }
209 10         33 delete @{$self->{items}}{@kill};
  10         36  
210              
211 10         36 $self-> rebuild_vectors;
212              
213             }
214              
215             sub rebuild_vectors
216             {
217 274     274 0 582 my $self = $_[0];
218 274         2148 $self-> {$_} = '' for qw(read write exc);
219 274         723 my $r = \ $self-> {read};
220 274         700 my $w = \ $self-> {write};
221 274         594 my $e = \ $self-> {exc};
222 274         586 while ( my ( $fileno, $bucket) = each %{$self->{items}}) {
  369         2106  
223 95         241 for my $flags ( map { $_-> [WATCH_IO_FLAGS] } @$bucket) {
  95         355  
224 95 100       565 vec($$r, $fileno, 1) = 1 if $flags & IO_READ;
225 95 100       398 vec($$w, $fileno, 1) = 1 if $flags & IO_WRITE;
226 95 50       487 vec($$e, $fileno, 1) = 1 if $flags & IO_EXCEPTION;
227             }
228             }
229             }
230              
231             1;
232              
233             __DATA__