Resolve a test hang when trying to run with POE_TRACE_DEFAULT=1 and no
[poe:poe-loop-event.git] / polo-event / lib / POE / Loop / Event.pm
1 # Event.pm event loop bridge for POE::Kernel.
2
3 # Empty package to appease perl.
4 package POE::Loop::Event;
5
6 use strict;
7
8 # Include common signal handling.  Signals should be safe now, and for
9 # some reason Event isn't dispatching SIGCHLD to me circa POE r2084.
10 use POE::Loop::PerlSignals;
11
12 use vars qw($VERSION);
13 $VERSION = '1.302'; # NOTE - Should be #.### (three decimal places)
14
15 =for poe_tests
16
17 sub skip_tests {
18   return "Event tests require the Event module" if (
19     do { eval "use Event"; $@ }
20   );
21   my $test_name = shift;
22   if ($test_name eq "k_signals_rerun" and $^O eq "MSWin32") {
23     return "This test crashes Perl when run with Tk on $^O";
24   }
25   if ($test_name eq "wheel_readline" and $^O eq "darwin") {
26     return "Event skips two of its own tests for the same reason";
27   }
28 }
29
30 =cut
31
32 # Everything plugs into POE::Kernel.
33 package POE::Kernel;
34
35 use strict;
36 use Event;
37
38 my $_watcher_timer;
39 my @fileno_watcher;
40 my %signal_watcher;
41
42 #------------------------------------------------------------------------------
43 # Loop construction and destruction.
44
45 sub loop_initialize {
46   my $self = shift;
47
48   $_watcher_timer = Event->timer(
49     cb     => \&_loop_event_callback,
50     desc   => 'dispatch_timer',
51     after  => 0,
52   );
53 }
54
55 sub loop_finalize {
56   my $self = shift;
57
58   foreach my $fd (0..$#fileno_watcher) {
59     next unless defined $fileno_watcher[$fd];
60     foreach my $mode (MODE_RD, MODE_WR, MODE_EX) {
61       POE::Kernel::_warn(
62         "Mode $mode watcher for fileno $fd is defined during loop finalize"
63       ) if defined $fileno_watcher[$fd]->[$mode];
64     }
65   }
66
67   $self->loop_ignore_all_signals();
68 }
69
70 #------------------------------------------------------------------------------
71 # Signal handler maintenance functions.
72
73 sub loop_attach_uidestroy {
74   # does nothing
75 }
76
77 #------------------------------------------------------------------------------
78 # Maintain time watchers.
79
80 sub loop_resume_time_watcher {
81   my ($self, $next_time) = @_;
82   ($_watcher_timer and $next_time) or return;
83   $_watcher_timer->at($next_time);
84   $_watcher_timer->start();
85 }
86
87 sub loop_reset_time_watcher {
88   my ($self, $next_time) = @_;
89   $_watcher_timer or return;
90   $self->loop_pause_time_watcher();
91   $self->loop_resume_time_watcher($next_time);
92 }
93
94 sub loop_pause_time_watcher {
95   $_watcher_timer or return;
96   $_watcher_timer->stop();
97 }
98
99 #------------------------------------------------------------------------------
100 # Maintain filehandle watchers.
101
102 sub loop_watch_filehandle {
103   my ($self, $handle, $mode) = @_;
104   my $fileno = fileno($handle);
105
106   # Overwriting a pre-existing watcher?
107   if (defined $fileno_watcher[$fileno]->[$mode]) {
108     $fileno_watcher[$fileno]->[$mode]->cancel();
109     undef $fileno_watcher[$fileno]->[$mode];
110   }
111
112   $fileno_watcher[$fileno]->[$mode] = Event->io(
113     fd => $fileno,
114     desc => "io_watcher $handle $fileno $mode",
115     poll => (
116       ( $mode == MODE_RD )
117       ? 'r'
118       : (
119         ( $mode == MODE_WR )
120         ? 'w'
121         : 'e'
122       )
123     ),
124     cb => \&_loop_select_callback,
125   );
126 }
127
128 sub loop_ignore_filehandle {
129   my ($self, $handle, $mode) = @_;
130   my $fileno = fileno($handle);
131
132   # Don't bother removing a select if none was registered.
133   if (defined $fileno_watcher[$fileno]->[$mode]) {
134     $fileno_watcher[$fileno]->[$mode]->cancel();
135     undef $fileno_watcher[$fileno]->[$mode];
136   }
137 }
138
139 sub loop_pause_filehandle {
140   my ($self, $handle, $mode) = @_;
141   my $fileno = fileno($handle);
142   $fileno_watcher[$fileno]->[$mode]->stop();
143 }
144
145 sub loop_resume_filehandle {
146   my ($self, $handle, $mode) = @_;
147   my $fileno = fileno($handle);
148   $fileno_watcher[$fileno]->[$mode]->start();
149 }
150
151 # Timer callback to dispatch events.
152
153 my $last_time = time();
154
155 sub _loop_event_callback {
156   my $self = $poe_kernel;
157
158   if (TRACE_STATISTICS) {
159     # TODO - I'm pretty sure the startup time will count as an unfair
160     # amount of idleness.
161     #
162     # TODO - Introducing many new time() syscalls.  Bleah.
163     $self->_data_stat_add('idle_seconds', time() - $last_time);
164   }
165
166   $self->_data_ev_dispatch_due();
167   $self->_test_if_kernel_is_idle();
168
169   # Transferring control back to Event; this is idle time.
170   $last_time = time() if TRACE_STATISTICS;
171 }
172
173 # Event filehandle callback to dispatch selects.
174 sub _loop_select_callback {
175   my $self = $poe_kernel;
176
177   my $event = shift;
178   my $watcher = $event->w;
179   my $fileno = $watcher->fd;
180   my $mode = (
181     ( $event->got eq 'r' )
182     ? MODE_RD
183     : (
184       ( $event->got eq 'w' )
185       ? MODE_WR
186       : (
187         ( $event->got eq 'e' )
188         ? MODE_EX
189         : return
190       )
191     )
192   );
193
194   $self->_data_handle_enqueue_ready($mode, $fileno);
195   $self->_test_if_kernel_is_idle();
196 }
197
198 #------------------------------------------------------------------------------
199 # The event loop itself.
200
201 sub loop_do_timeslice {
202   Event::one_event();
203 }
204
205 sub loop_run {
206   my $self = shift;
207
208   # Avoid a hang when trying to run an idle Kernel.
209   $self->_test_if_kernel_is_idle();
210
211   while ($self->_data_ses_count()) {
212     $self->loop_do_timeslice();
213   }
214 }
215
216 sub loop_halt {
217   $_watcher_timer->stop();
218   $_watcher_timer = undef;
219   Event::unloop_all(0);
220 }
221
222 1;
223
224 __END__
225
226 =head1 NAME
227
228 POE::Loop::Event - a bridge that allows POE to be driven by Event.pm
229
230 =head1 SYNOPSIS
231
232 See L<POE::Loop>.
233
234 =head1 DESCRIPTION
235
236 POE::Loop::Event implements the interface documented in L<POE::Loop>.
237 Therefore it has no documentation of its own.  Please see L<POE::Loop>
238 for more details.
239
240 =head1 SEE ALSO
241
242 L<POE>, L<POE::Loop>, L<Event>, L<POE::Loop::PerlSignals>
243
244 =head1 AUTHORS & LICENSING
245
246 Please see L<POE> for more information about authors, contributors,
247 and POE's licensing.
248
249 =cut
250
251 # rocco // vim: ts=2 sw=2 expandtab
252 # TODO - Edit.