| 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__ |