ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Impl/POE.pm
Revision: 1.27
Committed: Thu Jul 9 22:49:18 2009 UTC (14 years, 11 months ago) by root
Branch: MAIN
CVS Tags: rel-4_82
Changes since 1.26: +6 -6 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::Impl::POE - AnyEvent adaptor for POE
4
5 =encoding utf-8
6
7 =head1 SYNOPSIS
8
9 use AnyEvent;
10 use POE;
11
12 # this module gets loaded automatically as required
13
14 =head1 DESCRIPTION
15
16 This module provides transparent support for AnyEvent. You don't have to
17 do anything to make POE work with AnyEvent except by loading POE before
18 creating the first AnyEvent watcher. There are some cases where POE will
19 issue spurious (and non-suppressable) warnings. These can be avoided by
20 loading AnyEvent::Impl::POE before loading any other modules using POE and
21 AnyEvent, i.e. in your main program.
22
23 AnyEvent::Impl::POE will output some spurious message how to work around
24 POE's spurious messages when it detects these cases.
25
26 Unfortunately, POE isn't generic enough to implement a fully working
27 AnyEvent backend: POE is too badly designed, too badly documented and too
28 badly implemented.
29
30 Here are the details, and what it means to you if you want to be
31 interoperable with POE:
32
33 =over 4
34
35 =item Weird messages
36
37 If you only use C<run_one_timeslice> (as AnyEvent has to for it's
38 condition variables), POE will print an ugly, unsupressable, message at
39 program exit:
40
41 Sessions were started, but POE::Kernel's run() method was never...
42
43 The message is correct, the question is why POE prints it in the first
44 place in a correct program (this is not a singular case though).
45
46 The only way I found to work around this bug was to call C<<
47 ->run >> at AnyEvent loading time and stop the kernel immediately
48 again. Unfortunately, due to another design bug in POE, this cannot be
49 done (by documented means at least) without throwing away events in the
50 event queue.
51
52 The author of POE verified that this is indeed true, and has no plans to
53 change this.
54
55 This means that you will either have to live with lost events or you have
56 to make sure to load AnyEvent early enough (this is usually not that
57 difficult in a main program, but hard in a module).
58
59 POE has other weird messages, and sometimes weird behaviour, for example,
60 it doesn't support overloaded code references as callbacks for no apparent
61 reason.
62
63 =item One POE session per Event
64
65 AnyEvent has to create one POE::Session per event watcher, which is
66 immensely slow and makes watchers very large. The reason for this is
67 lacking lifetime management (mostly undocumented, too). Without one
68 session/watcher it is not possible to easily keep the kernel from running
69 endlessly.
70
71 This is not just a problem with the way AnyEvent has to interact with
72 POE, but is a principal issue with POEs lifetime management (namely
73 that stopping the kernel stops sessions, but AnyEvent has no control
74 over who and when the kernel starts or stops w.r.t. AnyEvent watcher
75 creation/destruction).
76
77 From benchmark data it is not clear that session creation is that costly,
78 though - the real inefficiencies with POE seem to come from other sources,
79 such as event handling.
80
81 =item One watcher per fd/event combo
82
83 POE, of course, suffers from the same bug as Tk and some other badly
84 designed event models in that it doesn't support multiple watchers per
85 fd/poll combo. The workaround is the same as with Tk: AnyEvent::Impl::POE
86 creates a separate file descriptor to hand to POE, which isn't fast and
87 certainly not nice to your resources.
88
89 Of course, without the workaround, POE also prints ugly messages again
90 that say the program *might* be buggy.
91
92 While this is not good to performance, at least regarding speed, with a
93 modern Linux kernel, the overhead is actually quite small.
94
95 =item Timing Deficiencies
96
97 POE manages to not have a function that returns the current time. This is
98 extremely problematic, as POE can use different time functions, which can
99 differ by more than a second - and user code is left guessing which one is
100 used.
101
102 In addition, most timer functions in POE want an absoltue timestamp, which
103 is hard to create if all you have is a relative time and no function to
104 return the "current time".
105
106 And of course POE doesn't handle time jumps at all (not even when using
107 an event loop that happens to do that, such as L<EV>, as it does its own
108 unoptimised timer management).
109
110 AnyEvent works around the unavailability of the current time using
111 relative timers exclusively, in the hope that POE gets it right at least
112 internally.
113
114 =item Event Non-Ordering
115
116 POE cannot guarentee the order of callback invocation for timers, and
117 usually gets it wrong. That is, if you have two timers, one timing out
118 after another (all else being equal), the callbacks might be called in
119 reverse order.
120
121 How one manages to even implement stuff that way escapes me.
122
123 =item Child Watchers
124
125 POE offers child watchers - which is a laudable thing, as few event loops
126 do. Unfortunately, they cannot even implement AnyEvent's simple child
127 watchers: they are not generic enough (the POE implementation isn't even
128 generic enough to let properly designed back-end use their native child
129 watcher instead - it insist on doing it itself the broken way).
130
131 Unfortunately, POE's child handling is inherently racy: if the child
132 exits before the handler is created (which is impossible to avoid in
133 general, imagine the forked program to exit immediately because of a
134 bug, or imagine the POE kernel being busy for a second), one has to
135 wait for another event to occur, which can take an indefinite amount of
136 time. Apparently POE implements a busy-waiting loop every second, but this
137 is not guaranteed or documented, so in practise child status events can be
138 delayed for up to a second "only".
139
140 Of course, whenever POE reaps an unrelated child it will also output a
141 message for it that you cannot suppress (which shouldn't be too surprising
142 at this point). Very professional.
143
144 As a workaround, AnyEvent::Impl::POE will take advantage of undocumented
145 behaviour in POE::Kernel to catch the status of all child processes, but
146 it cannot guarantee delivery.
147
148 How one manages to have such a glaring bug in an event loop after ten
149 years of development escapes me.
150
151 =item Documentation Quality
152
153 At the time of this writing, POE was in its tenth year. Still, its
154 documentation is extremely lacking, making it impossible to implement
155 stuff as trivial as AnyEvent watchers without having to resort to
156 undocumented behaviour or features.
157
158 For example, the POE::Kernel manpage has nine occurances of the word TODO
159 with an explanation of whats missing. In general, the POE manpages are
160 littered with comments like "section not yet written".
161
162 Some other gems:
163
164 This allows many object methods to also be package methods.
165
166 This is nice, but since it doesn't document I<which> methods these are,
167 this is utterly useless information.
168
169 Terminal signals will kill sessions if they are not handled by a
170 "sig_handled"() call. The OS signals that usually kill or dump a
171 process are considered terminal in POE, but they never trigger a
172 coredump. These are: HUP, INT, QUIT and TERM.
173
174 Although AnyEvent calls C<sig_handled>, removing it has no apparent
175 effects on POE handling SIGINT.
176
177 refcount_increment SESSION_ID, COUNTER_NAME
178
179 Nowhere is explained which COUNTER_NAMEs are valid and which aren't - not
180 all scalars (or even strings) are valid counter names. Take your guess,
181 failure is of course completely silent. I found this out the hard way, as
182 the first name I came up with was silently ignored.
183
184 get_next_event_time() returns the time the next event is due, in a form
185 compatible with the UNIX time() function.
186
187 And surely, one would hope that POE supports subsecond accuracy as
188 documented elsewhere, unlike the explanation above implies. Yet:
189
190 POE::Kernel timers support subsecond accuracy, but don’t expect too
191 much here. Perl is not the right language for realtime programming.
192
193 ... of course, Perl is not the right language to expect subsecond accuray
194 - the manpage author must hate Perl to spread so much FUD in so little
195 space. The Deliantra game server logs with 100µs-accuracy because Perl is
196 fast enough to require this, and is still able to deliver map updates with
197 little jitter at exactly the right time. It does not, however, use POE.
198
199 Furthermore, since the Kernel keeps track of everything sessions do, it
200 knows when a session has run out of tasks to perform.
201
202 This is impossible - how does the kernel know that a session is no longer
203 watching for some (external) event (e.g. by some other session)? It
204 cannot, and therefore this is wrong - but you would be hard pressed to
205 find out how to work around this and tell the kernel manually about such
206 events.
207
208 It gets worse, though - the notion of "task" or "resource", although used
209 throughout the documentation, is not defined in a usable way. For example,
210 waiting for a timeout is considered to be a task, waiting for a signal is
211 not (a session that only waits for a signal is considered finished and
212 gets removed). The user is left guessing when waiting for an event counts
213 as task and when not (in fact, the issue with signals is mentioned in
214 passing in a section about child watchers and directly contradicts earlier
215 parts in that document).
216
217 One could go on endlessly - ten years, no usable documentation.
218
219 It is likely that differences between documentation, or the one or two
220 things I had to guess, cause unanticipated problems with this adaptor.
221
222 =item Fragile and inconsistent API
223
224 The POE API is extremely inconsistent - sometimes you have to pass a
225 session argument, sometimes it gets ignored, sometimes a session-specific
226 method must not use a session argument.
227
228 Error handling is sub-standard as well: even for programming mistakes,
229 POE does not C<croak> but, in most cases, just sets C<$!> or simply does
230 nothing at all, leading to fragile programs.
231
232 Sometimes registering a handler uses the "eventname, parameter" ordering
233 (timeouts), sometimes it is "parameter, eventname" (signals). There is
234 little consistency overall.
235
236 =item Lack of knowledge
237
238 The IO::Poll event loop provides an alternative that theoretically
239 scales better than select().
240
241 The IO::Poll "event loop" (who in his right mind would call that an event
242 loop) of course scales about identically (sometimes it is a bit faster,
243 sometimes a bit slower) to select in theory, and also in practise, of
244 course, as both are O(n) in the number of file descriptors, which is
245 rather bad.
246
247 This is just one place where it gets obvious how little the author of the
248 POE manpage understands.
249
250 =item No idle events
251
252 The POE-recommended workaround to this is apparently to use
253 C<fork>. Consequently, idle watchera will have to be emulated by AnyEvent.
254
255 =back
256
257 On the good side, AnyEvent allows you to write your modules in a 100%
258 POE-compatible way (bug-for-bug compatible even), without forcing your
259 module to use POE - it is still open to better event models, of which
260 there are plenty.
261
262 =cut
263
264 package AnyEvent::Impl::POE;
265
266 no warnings;
267 use strict;
268
269 use AnyEvent ();
270 use POE;
271
272 # if POE is already running
273 if (${ $poe_kernel->[POE::Kernel::KR_RUN] } && POE::Kernel::KR_RUN_CALLED) {
274 print STDERR <<EOF;
275 POE is going to complain about
276 Sessions were started, but POE::Kernel's run() method was never...
277 Try putting:
278 use AnyEvent::Impl::POE;
279 at the very top of your main program to suppress these spurious warnings.
280 EOF
281 } else {
282 # workaround to suppress noise
283 POE::Session->create (inline_states => { _start => sub { @_[KERNEL]->stop } });
284 POE::Kernel->run;
285 }
286
287 sub io {
288 my ($class, %arg) = @_;
289
290 # POE itself might do the right thing, but some POE backends don't,
291 # so do the safe thing, it's not as if this will slow us down
292 # any further *g*
293 my ($fh, $pee) = AnyEvent::_dupfh $arg{poll}, $arg{fh}, "select_read", "select_write";
294
295 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
296
297 my $session = POE::Session->create (
298 inline_states => {
299 _start => sub { $_[KERNEL]->$pee ($fh => "ready") },
300 ready => sub { $cb->() },
301 stop => sub { $_[KERNEL]->$pee ($fh) },
302 },
303 );
304 bless \\$session, "AnyEvent::Impl::POE"
305 }
306
307 sub timer {
308 my ($class, %arg) = @_;
309
310 my $after = delete $arg{after};
311 my $ival = delete $arg{interval};
312 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
313
314 my $session = POE::Session->create (
315 inline_states => {
316 _start => sub {
317 $_[KERNEL]->delay_set (timeout => $after);
318 },
319 timeout => $ival ? sub { $_[KERNEL]->delay_set (timeout => $ival); $cb->() } : $cb,
320 stop => sub {
321 $_[KERNEL]->alarm_remove_all;
322 },
323 },
324 );
325 bless \\$session, "AnyEvent::Impl::POE"
326 }
327
328 sub signal {
329 my ($class, %arg) = @_;
330 my $signal = delete $arg{signal};
331 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
332 my $session = POE::Session->create (
333 inline_states => {
334 _start => sub {
335 $_[KERNEL]->sig ($signal => "catch");
336 $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
337 },
338 catch => sub {
339 $cb->();
340 $_[KERNEL]->sig_handled;
341 },
342 stop => sub {
343 $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
344 $_[KERNEL]->sig ($signal);
345 },
346 },
347 );
348 bless \\$session, "AnyEvent::Impl::POE"
349 }
350
351 sub child {
352 my ($class, %arg) = @_;
353 my $pid = delete $arg{pid};
354 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
355 my $session = POE::Session->create (
356 inline_states => {
357 _start => sub {
358 $_[KERNEL]->sig (CHLD => "child");
359 $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
360 },
361 child => sub {
362 my ($rpid, $status) = @_[ARG1, ARG2];
363
364 $cb->($rpid, $status) if $rpid == $pid || $pid == 0;
365 },
366 stop => sub {
367 $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
368 $_[KERNEL]->sig ("CHLD");
369 },
370 },
371 );
372 bless \\$session, "AnyEvent::Impl::POE"
373 }
374
375 sub DESTROY {
376 POE::Kernel->post (${${$_[0]}}, "stop");
377 }
378
379 sub one_event {
380 POE::Kernel->loop_do_timeslice;
381 }
382
383 sub loop {
384 POE::Kernel->run;
385 }
386
387 1;
388
389 =head1 SEE ALSO
390
391 L<AnyEvent>, L<POE>.
392
393 =head1 AUTHOR
394
395 Marc Lehmann <schmorp@schmorp.de>
396 http://home.schmorp.de/
397
398 =cut
399