File Coverage

blib/lib/Net/PSYC/Event/IO_Select.pm
Criterion Covered Total %
statement 97 126 76.9
branch 41 68 60.2
condition 31 43 72.0
subroutine 11 14 78.5
pod 0 9 0.0
total 180 260 69.2


line stmt bran cond sub pod time code
1             package Net::PSYC::Event::IO_Select;
2              
3             # TODO using fileno doesnt work for some funky file-handles ( perldoc -f fileno)
4             # but therefore select doesnt either. so.. who cares? In case someone knows a
5             # workaround for those, email me (I doubt that anybody is reading this anyway)
6              
7             our $VERSION = '0.4';
8              
9 3     3   14 use strict;
  3         6  
  3         86  
10              
11 3     3   13 use base qw(Exporter);
  3         7  
  3         249  
12 3     3   3481 use IO::Select;
  3         4541  
  3         184  
13 3     3   18 use Net::PSYC qw(W);
  3         7  
  3         22  
14              
15             sub BEGIN {
16 3 50   3   8 if (eval { Time::HiRes::time() }) {
  3         62  
17 0         0 eval qq {
18             sub mytime() { Time::HiRes::time() }
19             };
20             } else {
21 3     82 0 4308 eval qq {
  82         357  
22             sub mytime() { time() }
23             };
24             }
25             }
26              
27             our @EXPORT_OK = qw(init can_read can_write has_exception add remove start_loop stop_loop revoke);
28              
29             my (%S, %cb, $LOOP, @T);
30              
31             %cb = (
32             'r' => {},
33             'w' => {},
34             'e' => {},
35             );
36              
37             sub can_read {
38 0     0 0 0 $S{'r'}->can_read(@_);
39             }
40              
41             sub can_write {
42 0     0 0 0 $S{'w'}->can_write(@_);
43             }
44              
45             sub has_exception {
46 0     0 0 0 $S{'e'}->has_exception(@_);
47             }
48              
49             # add (\*fd, flags, cb, repeat)
50             sub add {
51 33     33 0 797 my ($fd, $flags, $cb, $repeat) = @_;
52              
53 33 50 33     189 unless ($cb && ref $cb eq 'CODE') {
54 0         0 W0('You need a proper callback for add()! (has to be a code-ref)');
55 0         0 return;
56             }
57              
58 33   100     176 W2('add(%s, %s, %p, %d)', $fd, $flags, $cb, $repeat||0);
59              
60 33   50     154 foreach (split(//, $flags || 'r')) {
61 33 100 100     245 if ($_ eq 'r' or $_ eq 'w' or $_ eq 'e') {
    50 66        
62 11 100       111 $S{$_} = new IO::Select() unless $S{$_};
63 11         81 $S{$_}->add($fd);
64 11         428 my $t = $S{$_}->[0];
65 11         41 vec($S{$_}->[0], fileno($fd), 1) = 1;
66             } elsif ($_ eq 't') {
67 22         27 my $i = 0;
68 22         705 my $t = mytime() + $fd;
69 22   66     85 while (exists $T[$i] && $T[$i]->[0] <= $t) {
70 1         4 $i++;
71             }
72 22 100       89 splice(@T, $i, 0, [$t, $cb, ($repeat) ? 1 : 0, $fd]);
73 22         122 return scalar($cb).$fd;
74 0         0 } else { next; }
75 11 100 66     95 $cb{$_}->{fileno($fd)} = [ (!defined($repeat) || $repeat) ? -1 : 1, $cb ];
76             }
77 11         34 1;
78             }
79              
80             sub revoke {
81 29     29 0 44 my $id = shift;
82 29         135 my $name = fileno($id);
83 29         71 W2('revoke(%s)', $name);
84 29         39 my @list;
85 29 100       59 if (@_) {
86 26         185 @list = @_;
87             } else {
88 3         13 @list = ('w', 'e', 'r');
89             }
90 29         55 foreach (@list) {
91 35 100 100     257 if (exists $cb{$_}->{$name} and $cb{$_}->{$name}[0] == 0) {
92 20         72 vec($S{$_}->[0], $name, 1) = 1;
93 20         49 $cb{$_}->{$name}[0] = 1;
94 20         53 W2('revoked %s', $id);
95             }
96             }
97             }
98              
99             # remove (\*fd[, flags] )
100             sub remove {
101 17     17 0 26 my $id = shift;
102 17         43 W2('remove(%s)', $id);
103              
104             # this is actually 'not so' smart. i will do a better one on request.
105 17 50       43 if (!ref $id) {
106 17         19 my $i = 0;
107 17         29 foreach (@T) {
108 17 50       74 if (scalar($T[$i]->[1]).$T[0]->[3] eq $id) {
109 17         24 splice(@T, $i, 1);
110 17         59 return 1;
111             }
112 0         0 $i++;
113             }
114             }
115              
116 0         0 my $name = fileno($id);
117 0         0 foreach ('w', 'e', 'r') {
118 0 0       0 if (exists $cb{$_}->{$name}) {
119 0 0 0     0 if (!$_[1] || $_[1] =~ /$_/) {
120 0         0 vec($S{$_}->[0], $name, 1) = 0;
121 0         0 $S{$_}->remove();
122             }
123             }
124             }
125             }
126              
127             sub start_loop {
128 3     3 0 470 my (@E, $sock, $name, @queue);
129            
130             # @queue
131            
132 3         5 $LOOP = 1;
133 3         8 my $time = undef;
134 3         17 LOOP: while ($LOOP) {
135 44 100 100     425 if (scalar(@T) && !scalar(@queue)) {
    100          
136 21         649 $time = $T[0]->[0] - mytime();
137 21 50       58 if ($time < 0) {
138 0         0 $time = 0;
139 0         0 @E = ([],[],[]);
140 0         0 goto TIME;
141             }
142             # we could do a goto here and leave out the select call. that
143             # however would keep rwe events from being called in case we have
144             # many many timers. As long as we dont have any means of handling
145             # different priorities we stay with this solution and try to be
146             # fair.
147             # TODO: think again
148             } elsif (scalar(@queue)) {
149 15         23 $time = 0;
150             } else {
151 8         13 $time = undef;
152             }
153              
154 44         189 my ($rmask, $wmask, $emask) = ($S{'r'}->[0], $S{'w'}->[0],
155             $S{'e'}->[0]);
156              
157 44 100 66     716 @E = IO::Select::select(defined($rmask) && $rmask =~ /[^\0]/
    100 100        
    50 33        
158             ? $S{'r'} : undef,
159             defined($wmask) && $wmask =~ /[^\0]/
160             ? $S{'w'} : undef,
161             defined($emask) && $emask =~ /[^\0]/
162             ? $S{'e'} : undef,
163             $time);
164              
165             TIME:
166 44   100     3536215 while (scalar(@T) && $T[0]->[0] <= mytime()) {
167 4         13 my $event = shift @T;
168 4 100 100     20 if ($event->[1]->() && $event->[2]) { # repeat!
169 1         10 add($event->[3], 't', $event->[1], 1);
170             }
171 4 50       1427 next LOOP unless ($time);
172             }
173              
174 44         67 foreach $sock (@{$E[0]}) { # read
  44         115  
175 9         26 $name = fileno($sock);
176 9 50       41 next unless (exists $cb{'r'}->{$name});
177 9         33 my $event = $cb{'r'}->{$name};
178            
179 9 50       28 if ($event->[0] != 0) { # repeat or not
180 9 50       24 if ($event->[0] > 0) {
181 0         0 $event->[0] = 0;
182 0         0 vec($S{'r'}->[0], $name, 1) = 0;
183             }
184            
185 9 100       41 if ($event->[1]->($sock) == -1) {
186 6         32 push(@queue, [$event->[1], $sock, 1]);
187             }
188             }
189             }
190              
191 44         71 foreach $sock (@{$E[1]}) { # write
  44         92  
192 26         50 $name = fileno($sock);
193 26 50       86 next unless (exists $cb{'w'}->{$name});
194 26         43 my $event = $cb{'w'}->{$name};
195            
196 26 50       67 if ($event->[0] != 0) { # repeat or not
197 26 50       60 if ($event->[0] > 0) {
198 26         37 $event->[0] = 0;
199 26         85 vec($S{'w'}->[0], $name, 1) = 0;
200             }
201              
202 26 50       112 if ($event->[1]->($sock) == -1) {
203 0         0 push(@queue, [$event->[1], $sock, 1]);
204             }
205             }
206             }
207              
208 44         74 foreach $sock (@{$E[2]}) { # error
  44         132  
209 0         0 $name = fileno($sock);
210 0 0       0 next unless (exists $cb{'e'}->{$name});
211 0         0 my $event = $cb{'e'}->{$name};
212              
213 0 0       0 if ($event->[0] != 0) { # repeat or not
214 0 0       0 if ($event->[0] > 0) {
215 0         0 $event->[0] = 0;
216 0         0 vec($S{'e'}->[0], $name, 1) = 0;
217             }
218              
219 0 0       0 if ($event->[1]->($sock) == -1) {
220 0         0 push(@queue, [$event->[1], $sock, 1]);
221             }
222             }
223             }
224              
225 44         165 foreach (0 .. $#queue) {
226 21         35 my $event = shift @queue;
227 21 100       84 if ($event->[0]->($event->[1], $event->[2]++) == -1) {
228 16         168 push(@queue, $event);
229             }
230             }
231             }
232 3         22 return 1;
233             }
234              
235             sub stop_loop {
236 3     3 0 25 $LOOP = 0;
237 3         17 return 1;
238             }
239              
240             1;