File Coverage

blib/lib/POE/Loop/IO_Poll.pm
Criterion Covered Total %
statement 126 145 86.9
branch 55 70 78.5
condition 19 32 59.3
subroutine 21 22 95.4
pod 0 14 0.0
total 221 283 78.0


line stmt bran cond sub pod time code
1             # IO::Poll event loop bridge for POE::Kernel. The theory is that this
2             # will be faster for large scale applications. This file is
3             # contributed by Matt Sergeant (baud).
4              
5             # Empty package to appease perl.
6             package POE::Loop::IO_Poll;
7              
8 53     53   968 use vars qw($VERSION);
  53         97  
  53         3198  
9             $VERSION = '1.365'; # NOTE - Should be #.### (three decimal places)
10              
11             # Include common signal handling.
12 53     53   20353 use POE::Loop::PerlSignals;
  52         114  
  52         1791  
13              
14             # Everything plugs into POE::Kernel;
15             package POE::Kernel;
16              
17             =for poe_tests
18              
19             sub skip_tests {
20             return "IO::Poll is not 100% compatible with $^O" if (
21             $^O eq "MSWin32" and not $ENV{POE_DANTIC}
22             );
23             return "IO::Poll tests require the IO::Poll module" if (
24             do { eval "use IO::Poll"; $@ }
25             );
26             }
27              
28             =cut
29              
30 52     52   294 use strict;
  52         109  
  52         1797  
31              
32             # Be sure we're using a contemporary version of IO::Poll. There were
33             # issues with certain versions of IO::Poll prior to 0.05. The latest
34             # version is 0.01, however.
35 52     52   1695 use IO::Poll 0.01;
  52         3044  
  52         6922  
36              
37             # Hand off to POE::Loop::Select if we're running under ActivePerl.
38             BEGIN {
39 52 50 33 52   1265 if ($^O eq "MSWin32" and not $ENV{POE_DANTIC}) {
40 0         0 warn "IO::Poll is defective on $^O. Falling back to IO::Select.\n";
41 0         0 require POE::Loop::Select;
42 0         0 POE::Loop::Select->import();
43 0         0 die "not really dying";
44             }
45             }
46              
47 52     52   275 use Errno qw(EINPROGRESS EWOULDBLOCK EINTR);
  52         72  
  52         3844  
48              
49 52         6079 use IO::Poll qw(
50             POLLRDNORM POLLWRNORM POLLRDBAND POLLERR POLLHUP POLLNVAL
51 52     52   261 );
  52         70  
52              
53             # Many systems' IO::Poll don't define POLLRDNORM.
54             # Usually upgrading IO::Poll helps.
55             BEGIN {
56 52     52   107 my $x = eval { POLLRDNORM };
  52         120  
57 52 50 33     68239 if ($@ or not defined $x) {
58 0         0 warn(
59             "Your IO::Poll doesn't define POLLRDNORM. Falling back to IO::Select.\n"
60             );
61 0         0 require POE::Loop::Select;
62 0         0 POE::Loop::Select->import();
63 0         0 die "not really dying";
64             }
65             }
66              
67             my %poll_fd_masks;
68              
69             # Allow $^T to change without affecting our internals.
70             my $start_time = monotime();
71              
72             #------------------------------------------------------------------------------
73             # Loop construction and destruction.
74              
75             sub loop_initialize {
76 96     96 0 148 my $self = shift;
77 96         259 %poll_fd_masks = ();
78             }
79              
80             sub loop_finalize {
81 73     73 0 150 my $self = shift;
82 73         293 $self->loop_ignore_all_signals();
83             }
84              
85             #------------------------------------------------------------------------------
86             # Signal handler maintenance functions.
87              
88 0     0 0 0 sub loop_attach_uidestroy {
89             # does nothing
90             }
91              
92             #------------------------------------------------------------------------------
93             # Maintain time watchers. For this loop, we simply save the next
94             # event time in a scalar. loop_do_timeslice() will use the saved
95             # value. A "paused" time watcher is just a timeout for some future
96             # time.
97              
98             my $_next_event_time = monotime();
99              
100             sub loop_resume_time_watcher {
101 508     508 0 1103 $_next_event_time = $_[1];
102             }
103              
104             sub loop_reset_time_watcher {
105 463     463 0 1881 $_next_event_time = $_[1];
106             }
107              
108             sub loop_pause_time_watcher {
109 335     335 0 1978 $_next_event_time = monotime() + 3600;
110             }
111              
112             # A static function; not some object method.
113              
114             sub mode_to_poll {
115 1268 100   1268 0 3370 return POLLRDNORM if $_[0] == MODE_RD;
116 732 50       2145 return POLLWRNORM if $_[0] == MODE_WR;
117 0 0       0 return POLLRDBAND if $_[0] == MODE_EX;
118 0         0 croak "unknown I/O mode $_[0]";
119             }
120              
121             #------------------------------------------------------------------------------
122             # Maintain filehandle watchers.
123              
124             sub loop_watch_filehandle {
125 426     426 0 685 my ($self, $handle, $mode) = @_;
126 426         656 my $fileno = fileno($handle);
127              
128 426         1127 my $type = mode_to_poll($mode);
129 426   100     2108 my $current = $poll_fd_masks{$fileno} || 0;
130 426         599 my $new = $current | $type;
131              
132 426         368 if (TRACE_FILES) {
133 426         2067 POE::Kernel::_warn(
134             sprintf(
135             " Watch $fileno: " .
136             "Current mask: 0x%02X - including 0x%02X = 0x%02X\n",
137             $current, $type, $new
138             )
139             );
140             }
141              
142 202         1133 $poll_fd_masks{$fileno} = $new;
143             }
144              
145             sub loop_ignore_filehandle {
146 344     344 0 529 my ($self, $handle, $mode) = @_;
147 344         639 my $fileno = fileno($handle);
148              
149 344         734 my $type = mode_to_poll($mode);
150 344   100     1188 my $current = $poll_fd_masks{$fileno} || 0;
151 344         523 my $new = $current & ~$type;
152              
153 344         323 if (TRACE_FILES) {
154 344         1358 POE::Kernel::_warn(
155             sprintf(
156             " Ignore $fileno: " .
157             ": Current mask: 0x%02X - removing 0x%02X = 0x%02X\n",
158             $current, $type, $new
159             )
160             );
161             }
162              
163 131 100       902 if ($new) {
164 216         492 $poll_fd_masks{$fileno} = $new;
165             }
166             else {
167 127         496 delete $poll_fd_masks{$fileno};
168             }
169             }
170              
171             sub loop_pause_filehandle {
172 277     277 0 402 my ($self, $handle, $mode) = @_;
173 277         412 my $fileno = fileno($handle);
174              
175 277         531 my $type = mode_to_poll($mode);
176 277   50     799 my $current = $poll_fd_masks{$fileno} || 0;
177 277         425 my $new = $current & ~$type;
178              
179 277         260 if (TRACE_FILES) {
180 277         1160 POE::Kernel::_warn(
181             sprintf(
182             " Pause $fileno: " .
183             ": Current mask: 0x%02X - removing 0x%02X = 0x%02X\n",
184             $current, $type, $new
185             )
186             );
187             }
188              
189 225 100       601 if ($new) {
190 149         422 $poll_fd_masks{$fileno} = $new;
191             }
192             else {
193 54         213 delete $poll_fd_masks{$fileno};
194             }
195             }
196              
197             sub loop_resume_filehandle {
198 221     221 0 338 my ($self, $handle, $mode) = @_;
199 221         320 my $fileno = fileno($handle);
200              
201 221         392 my $type = mode_to_poll($mode);
202 221   100     762 my $current = $poll_fd_masks{$fileno} || 0;
203 221         270 my $new = $current | $type;
204              
205 221         202 if (TRACE_FILES) {
206 221         1031 POE::Kernel::_warn(
207             sprintf(
208             " Resume $fileno: " .
209             "Current mask: 0x%02X - including 0x%02X = 0x%02X\n",
210             $current, $type, $new
211             )
212             );
213             }
214              
215 151         602 $poll_fd_masks{$fileno} = $new;
216             }
217              
218             #------------------------------------------------------------------------------
219             # The event loop itself.
220              
221             sub loop_do_timeslice {
222 736     736 0 906 my $self = shift;
223              
224             # Check for a hung kernel.
225 736         2465 $self->_test_if_kernel_is_idle();
226              
227             # Set the poll timeout based on current queue conditions. If there
228             # are FIFO events, then the poll timeout is zero and move on.
229             # Otherwise set the poll timeout until the next pending event, if
230             # there are any. If nothing is waiting, set the timeout for some
231             # constant number of seconds.
232              
233 736         1057 my $timeout = $_next_event_time;
234              
235 736         1658 my $now = monotime();
236 736 50       1446 if (defined $timeout) {
237 736         1064 $timeout -= $now;
238 736 100       1810 $timeout = 0 if $timeout < 0;
239             }
240             else {
241 0         0 die "shouldn't happen" if ASSERT_DATA;
242 0         0 $timeout = 3600;
243             }
244              
245 736         669 if (TRACE_EVENTS) {
246 736         6572 POE::Kernel::_warn(
247             ' Kernel::run() iterating. ' .
248             sprintf(
249             "now(%.4f) timeout(%.4f) then(%.4f)\n",
250             $now-$start_time, $timeout, ($now-$start_time)+$timeout
251             )
252             );
253             }
254              
255 736         2807 if (TRACE_FILES) {
256 736         100470453 foreach (sort { $a<=>$b} keys %poll_fd_masks) {
  1426         2706  
257 1331         1932 my @types;
258 1080 100       8501 push @types, "plain-file" if -f;
259 1326 100       3738 push @types, "directory" if -d;
260 1326 100       4087 push @types, "symlink" if -l;
261 1191 100       2895 push @types, "pipe" if -p;
262 1261 100       3430 push @types, "socket" if -S;
263 1200 50       3121 push @types, "block-special" if -b;
264 1648 100       3947 push @types, "character-special" if -c;
265 1282 100       19922 push @types, "tty" if -t;
266 1282         1754 my @modes;
267 1226         1824 my $flags = $poll_fd_masks{$_};
268 1203 100       2895 push @modes, 'r' if $flags & (POLLRDNORM | POLLHUP | POLLERR);
269 1259 100       2414 push @modes, 'w' if $flags & (POLLWRNORM | POLLHUP | POLLERR);
270 1137 50       1780 push @modes, 'x' if $flags & (POLLRDBAND | POLLHUP | POLLERR);
271 1114         4972 POE::Kernel::_warn(
272             " file descriptor $_ = modes(@modes) types(@types)\n"
273             );
274             }
275             }
276              
277             # Avoid looking at filehandles if we don't need to.
278             # TODO The added code to make this sleep is non-optimal. There is a
279             # way to do this in fewer tests.
280              
281 664 50       3477 if (scalar keys %poll_fd_masks) {
    0          
282              
283             # There are filehandles to poll, so do so.
284              
285             # Check filehandles, or wait for a period of time to elapse.
286 476         33320452 my $hits = IO::Poll::_poll($timeout * 1000, my @results = %poll_fd_masks);
287              
288 476         919 if (ASSERT_FILES) {
289 596 100       1948 if ($hits < 0) {
290 124 100 33     438 POE::Kernel::_trap(" poll returned $hits (error): $!")
      66        
      33        
291             unless ( ($! == EINPROGRESS) or
292             ($! == EWOULDBLOCK) or
293             ($! == EINTR) or
294             ($! == 0) # SIGNAL_PIPE strangeness
295             );
296             }
297             }
298              
299 582         1025 if (TRACE_FILES) {
300 462 100       1397 if ($hits > 0) {
    100          
301 173         676 POE::Kernel::_warn " poll hits = $hits\n";
302             }
303             elsif ($hits == 0) {
304 304         1020 POE::Kernel::_warn " poll timed out...\n";
305             }
306             }
307              
308             # If poll has seen filehandle activity, then gather up the
309             # active filehandles and synchronously dispatch events to the
310             # appropriate handlers.
311              
312 717 100       3776 if ($hits > 0) {
313              
314             # This is where they're gathered.
315              
316 154         189 my (@rd_ready, @wr_ready, @ex_ready);
317 154         763 my %poll_fd_results = @results;
318 154         576 while (my ($fd, $got_mask) = each %poll_fd_results) {
319 709 100       1666 next unless $got_mask;
320              
321 318         422 my $watch_mask = $poll_fd_masks{$fd};
322 318 100 100     1377 if (
323             $watch_mask & POLLRDNORM and
324             $got_mask & (POLLRDNORM | POLLHUP | POLLERR | POLLNVAL)
325             ) {
326 162         181 if (TRACE_FILES) {
327 162         1243 POE::Kernel::_warn " enqueuing read for fileno $fd";
328             }
329              
330 162         630 push @rd_ready, $fd;
331             }
332              
333 318 100 66     1122 if (
334             $watch_mask & POLLWRNORM and
335             $got_mask & (POLLWRNORM | POLLHUP | POLLERR | POLLNVAL)
336             ) {
337 157         132 if (TRACE_FILES) {
338 157         472 POE::Kernel::_warn " enqueuing write for fileno $fd";
339             }
340              
341 157         401 push @wr_ready, $fd;
342             }
343              
344 318 50 33     2290 if (
345             $watch_mask & POLLRDBAND and
346             $got_mask & (POLLRDBAND | POLLHUP | POLLERR | POLLNVAL)
347             ) {
348 0         0 if (TRACE_FILES) {
349 0         0 POE::Kernel::_warn " enqueuing expedite for fileno $fd";
350             }
351              
352 0         0 push @ex_ready, $fd;
353             }
354             }
355              
356 154 100       3609 @rd_ready and $self->_data_handle_enqueue_ready(MODE_RD, @rd_ready);
357 154 100       604 @wr_ready and $self->_data_handle_enqueue_ready(MODE_WR, @wr_ready);
358 154 50       872 @ex_ready and $self->_data_handle_enqueue_ready(MODE_EX, @ex_ready);
359             }
360             }
361             elsif ($timeout) {
362              
363             # No filehandles to poll on. Try to sleep instead. Use sleep()
364             # itself on MSWin32. Use a dummy four-argument select() everywhere
365             # else.
366              
367 0 0       0 if ($^O eq 'MSWin32') {
368 0         0 sleep($timeout);
369             }
370             else {
371 0         0 CORE::select(undef, undef, undef, $timeout);
372             }
373             }
374              
375             # Dispatch whatever events are due.
376 462         1992 $self->_data_ev_dispatch_due();
377             }
378              
379             ### Run for as long as there are sessions to service.
380              
381             sub loop_run {
382 73     73 0 281 my $self = shift;
383 73         236 while ($self->_data_ses_count()) {
384 736         1638 $self->loop_do_timeslice();
385             }
386             }
387              
388 73     73 0 165 sub loop_halt {
389             # does nothing
390             }
391              
392             1;
393              
394             __END__