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   146 use strict;
  27         42  
  27         700  
5 27     27   129 use warnings;
  27         47  
  27         895  
6 27     27   137 use Errno qw(EINTR EAGAIN);
  27         38  
  27         3833  
7 27     27   157 use IO::Lambda qw(:constants);
  27         54  
  27         4852  
8 27     27   137 use Time::HiRes qw(time);
  27         51  
  27         223  
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 83 my $self = bless {} , shift;
20 27         217 $self-> {$_} = '' for qw(read write exc);
21 27         65 $self-> {items} = {};
22 27         64 $self-> {timers} = [];
23 27         180 return $self;
24             }
25              
26             sub empty
27             {
28 487807     487807 1 599639 my $self = shift;
29             return (
30 487807         789069 @{$self->{timers}} +
31 487807 100       487231 keys(%{$self-> {items}})
  487807         1829823  
32             ) ? 0 : 1;
33             }
34              
35             sub yield
36             {
37 250     250 1 593 my ( $self, $nonblocking ) = @_;
38              
39 250 50       697 return if $self-> empty;
40              
41 250         420 my $t;
42 250 50       801 $t = 0 if $nonblocking;
43              
44 250         458 my ($min,$max) = ( 0, -1);
45 250         902 my $ct = time;
46              
47             # timers
48 250         378 for ( @{$self-> {timers}}) {
  250         734  
49 136 100 100     1284 $t = $_->[WATCH_DEADLINE]
      33        
50             if defined $_->[WATCH_DEADLINE] and
51             (!defined($t) or $t > $_-> [WATCH_DEADLINE]);
52             }
53              
54             # handles
55 250         444 my ( $R, $W, $E) = @{$self}{qw(read write exc)};
  250         890  
56              
57 250         605 while ( my ( $fileno, $bucket) = each %{ $self-> {items}} ) {
  567         2058  
58 317         601 for ( @$bucket) {
59 317 100 100     1033 $t = $_->[WATCH_DEADLINE]
      66        
60             if defined $_->[WATCH_DEADLINE] and
61             (!defined($t) or $t > $_-> [WATCH_DEADLINE]);
62             }
63 317 50       768 warn "select: fileno $fileno\n" if $DEBUG;
64 317 100       1078 $max = $fileno if $max < $fileno;
65 317 50 33     2262 $min = $fileno if !defined($min) or $min > $fileno;
66             }
67 250 100       758 if ( defined $t) {
    50          
68 109         248 $t -= $ct;
69 109 100       376 $t = 0 if $t < 0;
70 109 50       337 warn "select: timeout=$t\n" if $DEBUG;
71             } elsif ( $DEBUG) {
72 0         0 warn "select: no timeout\n";
73             }
74              
75             # do select
76 250         5772747 my $n = select( $R, $W, $E, $t);
77 250 50       1183 warn "select: $n handles ready\n" if $DEBUG;
78 250 100       740 if ( $n < 0) {
79 1 50 33     41 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 250         709 my ( @kill, @expired);
103              
104 250         719 $t = $self-> {timers};
105             @$t = grep {
106 250 100       823 ($$_[WATCH_DEADLINE] <= $ct) ? do {
  136         1007  
107 47         158 push @expired, $_;
108 47         177 0;
109             } : 1;
110             } @$t;
111              
112             # handles
113 250 100       709 if ( $n > 0) {
114             # process selected handles
115 165   100     1527 for ( my $i = $min; $i <= $max && $n > 0; $i++) {
116 1750         3590 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 1750 100       9407 next unless $what;
122              
123 212         467 my $bucket = $self-> {items}-> {$i};
124             @$bucket = grep {
125 212 50       496 ($$_[WATCH_IO_FLAGS] & $what) ? do {
  212         751  
126 212         370 $$_[WATCH_IO_FLAGS] &= $what;
127 212         350 push @expired, $_;
128 212         729 0;
129             } : 1;
130             } @$bucket;
131 212 50       746 delete $self-> {items}->{$i} unless @$bucket;
132 212         1264 $n--;
133             }
134             } else {
135             # else process timeouts
136 85         242 my @kill;
137 85         219 while ( my ( $fileno, $bucket) = each %{ $self-> {items}}) {
  93         605  
138             @$bucket = grep {
139 8         27 (
140             defined($_->[WATCH_DEADLINE]) &&
141             $_->[WATCH_DEADLINE] <= $ct
142 8 100 100     80 ) ? do {
143 1         5 $$_[WATCH_IO_FLAGS] = 0;
144 1         4 push @expired, $_;
145 1         6 0;
146             } : 1;
147             } @$bucket;
148 8 100       51 push @kill, $fileno unless @$bucket;
149             }
150 85         176 delete @{$self->{items}}{@kill};
  85         281  
151             }
152 250         1066 $self-> rebuild_vectors;
153            
154             # call them
155 250         1904 $$_[WATCH_OBJ]-> io_handler( $_) for @expired;
156             }
157              
158             sub watch
159             {
160 218     218 1 368 my ( $self, $rec) = @_;
161 218         602 my $fileno = fileno $rec->[WATCH_IO_HANDLE];
162 218 50       576 die "Invalid filehandle" unless defined $fileno;
163 218         320 my $flags = $rec->[WATCH_IO_FLAGS];
164              
165 218 100       1370 vec($self-> {read}, $fileno, 1) = 1 if $flags & IO_READ;
166 218 100       681 vec($self-> {write}, $fileno, 1) = 1 if $flags & IO_WRITE;
167 218 100       739 vec($self-> {exc}, $fileno, 1) = 1 if $flags & IO_EXCEPTION;
168              
169 218         287 push @{$self-> {items}-> {$fileno}}, $rec;
  218         1293  
170             }
171              
172             sub after
173             {
174 58     58 1 170 my ( $self, $rec) = @_;
175 58         98 push @{$self-> {timers}}, $rec;
  58         240  
176             }
177              
178             sub remove
179             {
180 46     46 1 153 my ($self, $obj) = @_;
181              
182 46         168 @{$self-> {timers}} = grep {
183 7 50       105 defined($_->[WATCH_OBJ]) and $_->[WATCH_OBJ] != $obj
184 46         134 } @{$self-> {timers}};
  46         170  
185              
186 46         120 my @kill;
187 46         109 while ( my ( $fileno, $bucket) = each %{$self->{items}}) {
  71         499  
188 25 50       77 @$bucket = grep { defined($_->[WATCH_OBJ]) and $_->[WATCH_OBJ] != $obj } @$bucket;
  25         309  
189 25 100       161 next if @$bucket;
190 5         27 push @kill, $fileno;
191             }
192 46         126 delete @{$self->{items}}{@kill};
  46         179  
193              
194 46         228 $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         35  
  7         36  
  10         193  
202              
203 10         25 my @kill;
204 10         31 while ( my ( $fileno, $bucket) = each %{$self->{items}}) {
  12         84  
205 2         17 @$bucket = grep { $_ != $rec } @$bucket;
  2         8  
206 2 50       11 next if @$bucket;
207 0         0 push @kill, $fileno;
208             }
209 10         27 delete @{$self->{items}}{@kill};
  10         38  
210              
211 10         39 $self-> rebuild_vectors;
212              
213             }
214              
215             sub rebuild_vectors
216             {
217 306     306 0 640 my $self = $_[0];
218 306         2511 $self-> {$_} = '' for qw(read write exc);
219 306         685 my $r = \ $self-> {read};
220 306         694 my $w = \ $self-> {write};
221 306         658 my $e = \ $self-> {exc};
222 306         633 while ( my ( $fileno, $bucket) = each %{$self->{items}}) {
  432         2334  
223 126         322 for my $flags ( map { $_-> [WATCH_IO_FLAGS] } @$bucket) {
  126         439  
224 126 100       629 vec($$r, $fileno, 1) = 1 if $flags & IO_READ;
225 126 100       430 vec($$w, $fileno, 1) = 1 if $flags & IO_WRITE;
226 126 50       1288 vec($$e, $fileno, 1) = 1 if $flags & IO_EXCEPTION;
227             }
228             }
229             }
230              
231             1;
232              
233             __DATA__