ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Impl/POE.pm
Revision: 1.46
Committed: Mon Apr 9 02:25:48 2012 UTC (12 years, 2 months ago) by root
Branch: MAIN
CVS Tags: rel-7_05, rel-7_07, rel-7_01, rel-7_02, rel-7_03, rel-7_08, rel-7_09, rel-7_16, rel-7_13, rel-7_11, rel-7_15, rel-7_14, rel-7_12, rel-7_0, rel-7_04, HEAD
Changes since 1.45: +1 -1 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-suppressible) 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 its
38 condition variables), POE will print an ugly, unsuppressible, 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 AnyEvent consequently patches the POE kernel so it thinks it already
47 ran. Other workarounds, even the one cited in the POE documentation
48 itself, have serious side effects, such as throwing away events.
49
50 The author of POE verified that this is indeed true, and has no plans to
51 change this.
52
53 POE has other weird messages, and sometimes weird behaviour, for example,
54 it doesn't support overloaded code references as callbacks for no apparent
55 reason.
56
57 =item One POE session per Event
58
59 AnyEvent has to create one POE::Session per event watcher, which is
60 immensely slow and makes watchers very large. The reason for this is
61 lacking lifetime management (mostly undocumented, too). Without one
62 session/watcher it is not possible to easily keep the kernel from running
63 endlessly.
64
65 This is not just a problem with the way AnyEvent has to interact with
66 POE, but is a principal issue with POEs lifetime management (namely
67 that stopping the kernel stops sessions, but AnyEvent has no control
68 over who and when the kernel starts or stops w.r.t. AnyEvent watcher
69 creation/destruction).
70
71 From benchmark data it is not clear that session creation is that costly,
72 though - the real inefficiencies with POE seem to come from other sources,
73 such as event handling.
74
75 =item One watcher per fd/event combo
76
77 POE, of course, suffers from the same bug as Tk and some other badly
78 designed event models in that it doesn't support multiple watchers per
79 fd/poll combo. The workaround is the same as with Tk: AnyEvent::Impl::POE
80 creates a separate file descriptor to hand to POE, which isn't fast and
81 certainly not nice to your resources.
82
83 Of course, without the workaround, POE also prints ugly messages again
84 that say the program *might* be buggy.
85
86 While this is not good to performance, at least regarding speed, with a
87 modern Linux kernel, the overhead is actually quite small.
88
89 =item Timing deficiencies
90
91 POE manages to not have a function that returns the current time. This is
92 extremely problematic, as POE can use different time functions, which can
93 differ by more than a second - and user code is left guessing which one is
94 used.
95
96 In addition, most timer functions in POE want an absolute timestamp, which
97 is hard to create if all you have is a relative time and no function to
98 return the "current time".
99
100 And of course POE doesn't handle time jumps at all (not even when using
101 an event loop that happens to do that, such as L<EV>, as it does its own
102 unoptimised timer management).
103
104 AnyEvent works around the unavailability of the current time using
105 relative timers exclusively, in the hope that POE gets it right at least
106 internally.
107
108 =item Lack of defined event ordering
109
110 POE cannot guarantee the order of callback invocation for timers, and
111 usually gets it wrong. That is, if you have two timers, one timing out
112 after another (all else being equal), the callbacks might be called in
113 reverse order.
114
115 How one manages to even implement stuff that way escapes me.
116
117 =item Child watchers
118
119 POE offers child watchers - which is a laudable thing, as few event loops
120 do. Unfortunately, they cannot even implement AnyEvent's simple child
121 watchers: they are not generic enough (the POE implementation isn't even
122 generic enough to let properly designed back-end use their native child
123 watcher instead - it insist on doing it itself the broken way).
124
125 Unfortunately, POE's child handling is inherently racy: if the child exits
126 before the handler is created (because e.g. it crashes or simply is quick
127 about it), then current versions of POE (1.352) will I<never> invoke the
128 child watcher, and there is nothing that can be done about it. Older
129 versions of POE only delayed in this case. The reason is that POE first
130 checks if the child has already exited, and I<then> installs the signal
131 handler - aa classical race.
132
133 Your only hope is for the fork'ed process to not exit too quickly, in
134 which case everything happens to work.
135
136 Of course, whenever POE reaps an unrelated child it will also output a
137 message for it that you cannot suppress (which shouldn't be too surprising
138 at this point). Very professional.
139
140 As a workaround, AnyEvent::Impl::POE will take advantage of undocumented
141 behaviour in POE::Kernel to catch the status of all child processes, but
142 it cannot guarantee delivery.
143
144 How one manages to have such a glaring bug in an event loop after ten
145 years of development escapes me.
146
147 (There are more annoying bugs, for example, POE runs C<waitpid>
148 unconditionally at finaliser time, so your program will hang until all
149 child processes have exited.)
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 occurrences of the word TODO
159 with an explanation of whats missing. In general, the POE man pages 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 sub-second 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 sub-second
194 accuracy - the manpage author must hate Perl to spread so much FUD in
195 so little space. The Deliantra game server logs with 100µs-accuracy
196 because Perl is fast enough to require this, and is still able to deliver
197 map updates with little jitter at exactly the right time. It does not,
198 however, use POE.
199
200 Furthermore, since the Kernel keeps track of everything sessions do, it
201 knows when a session has run out of tasks to perform.
202
203 This is impossible - how does the kernel know that a session is no longer
204 watching for some (external) event (e.g. by some other session)? It
205 cannot, and therefore this is wrong - but you would be hard pressed to
206 find out how to work around this and tell the kernel manually about such
207 events.
208
209 It gets worse, though - the notion of "task" or "resource", although used
210 throughout the documentation, is not defined in a usable way. For example,
211 waiting for a timeout is considered to be a task, waiting for a signal is
212 not (a session that only waits for a signal is considered finished and
213 gets removed). The user is left guessing when waiting for an event counts
214 as task and when not (in fact, the issue with signals is mentioned in
215 passing in a section about child watchers and directly contradicts earlier
216 parts in that document).
217
218 One could go on endlessly - ten years, no usable documentation.
219
220 It is likely that differences between documentation, or the one or two
221 things I had to guess, cause unanticipated problems with this adaptor.
222
223 =item Fragile and inconsistent API
224
225 The POE API is extremely inconsistent - sometimes you have to pass a
226 session argument, sometimes it gets ignored, sometimes a session-specific
227 method must not use a session argument.
228
229 Error handling is sub-standard as well: even for programming mistakes,
230 POE does not C<croak> but, in most cases, just sets C<$!> or simply does
231 nothing at all, leading to fragile programs.
232
233 Sometimes registering a handler uses the "eventname, parameter" ordering
234 (timeouts), sometimes it is "parameter, eventname" (signals). There is
235 little consistency overall.
236
237 =item Lack of knowledge
238
239 The IO::Poll event loop provides an alternative that theoretically
240 scales better than select().
241
242 The IO::Poll "event loop" (who in his right mind would call that an event
243 loop) of course scales about identically (sometimes it is a bit faster,
244 sometimes a bit slower) to select in theory, and also in practise, of
245 course, as both are O(n) in the number of file descriptors, which is
246 rather bad.
247
248 This is just one place where it gets obvious how little the author of the
249 POE manpage understands.
250
251 =item No idle events
252
253 The POE-recommended workaround to this is apparently to use
254 C<fork>. Consequently, idle watchers will have to be emulated by AnyEvent.
255
256 =item Questionable maintainer behaviour
257
258 The author of POE is known to fabricate statements and post these to
259 public mailinglists - apparently, spreading FUD about competing (in his
260 eyes) projects or their maintainers is acceptable to him.
261
262 This has (I believe) zero effects on the quality or usefulness of his
263 code, but it does completely undermine his trustworthyness - so don't
264 blindly believe anything he says, he might have just made it up to suit
265 his needs (benchmark results, the names of my ten wifes, the length of my
266 penis, etc. etc.). When in doubt, double-check - not just him, anybody
267 actually.
268
269 Example: L<http://www.nntp.perl.org/group/perl.perl5.porters/2012/01/msg182141.html>.
270 I challenged him in that thread to provide evidence for his statement by giving at
271 least two examples, but of course since he just made it up, he couldn't provide any evidence.
272
273 =back
274
275 On the good side, AnyEvent allows you to write your modules in a 100%
276 POE-compatible way (bug-for-bug compatible even), without forcing your
277 module to use POE - it is still open to better event models, of which
278 there are plenty.
279
280 Oh, and one other positive thing:
281
282 RUNNING_IN_HELL
283
284 POE knows about the nature of the beast!
285
286 =cut
287
288 package AnyEvent::Impl::POE;
289
290 use AnyEvent (); BEGIN { AnyEvent::common_sense }
291 use POE;
292
293 # suppress an idiotic warning inside POE
294 ${ POE::Kernel->new->[POE::Kernel::KR_RUN] } |= POE::Kernel::KR_RUN_CALLED;
295
296 sub io {
297 my ($class, %arg) = @_;
298
299 # POE itself might do the right thing, but some POE backends don't,
300 # so do the safe thing, it's not as if this will slow us down
301 # any further *g*
302 my ($fh, $pee) = AnyEvent::_dupfh $arg{poll}, $arg{fh}, "select_read", "select_write";
303
304 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
305
306 my $session = POE::Session->create (
307 inline_states => {
308 _start => sub { $_[KERNEL]->$pee ($fh => "ready") },
309 ready => sub { $cb->() },
310 stop => sub { $_[KERNEL]->$pee ($fh) },
311 },
312 );
313 bless \\$session, "AnyEvent::Impl::POE"
314 }
315
316 sub timer {
317 my ($class, %arg) = @_;
318
319 my $after = delete $arg{after};
320 my $ival = delete $arg{interval};
321 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
322
323 my $session = POE::Session->create (
324 inline_states => {
325 _start => sub {
326 $_[KERNEL]->delay_set (timeout => $after);
327 },
328 timeout => $ival ? sub { $_[KERNEL]->delay_set (timeout => $ival); $cb->() } : $cb,
329 stop => sub {
330 $_[KERNEL]->alarm_remove_all;
331 },
332 },
333 );
334 bless \\$session, "AnyEvent::Impl::POE"
335 }
336
337 sub signal {
338 my ($class, %arg) = @_;
339 my $signal = AnyEvent::Base::sig2name delete $arg{signal};
340 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
341 my $session = POE::Session->create (
342 inline_states => {
343 _start => sub {
344 # I suck - POE
345 },
346 start => sub {
347 $_[KERNEL]->sig ($signal => "catch");
348 $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
349 },
350 catch => sub {
351 $cb->();
352 $_[KERNEL]->sig_handled;
353 },
354 stop => sub {
355 $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
356 $_[KERNEL]->sig ($signal);
357 },
358 },
359 );
360 POE::Kernel->call ($session, "start");
361 bless \\$session, "AnyEvent::Impl::POE"
362 }
363
364 sub child {
365 my ($class, %arg) = @_;
366 my $pid = delete $arg{pid};
367 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
368 my $session = POE::Session->create (
369 inline_states => {
370 _start => sub {
371 # I suck - POE
372 },
373 start => sub {
374 $_[KERNEL]->sig (CHLD => "child");
375 $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
376 },
377 child => sub {
378 my ($rpid, $status) = @_[ARG1, ARG2];
379
380 $cb->($rpid, $status) if $rpid == $pid || $pid == 0;
381 },
382 stop => sub {
383 $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
384 $_[KERNEL]->sig ("CHLD");
385 },
386 },
387 );
388 # newer POE versions lose signals unless we call ->sig early.
389 POE::Kernel->call ($session, "start");
390 bless \\$session, "AnyEvent::Impl::POE"
391 }
392
393 sub DESTROY {
394 POE::Kernel->call (${${$_[0]}}, "stop");
395 }
396
397 #sub loop {
398 # POE::Kernel->run;
399 #}
400
401 sub _poll {
402 POE::Kernel->loop_do_timeslice;
403 }
404
405 sub AnyEvent::CondVar::Base::_wait {
406 POE::Kernel->loop_do_timeslice until exists $_[0]{_ae_sent};
407 }
408
409 =head1 SEE ALSO
410
411 L<AnyEvent>, L<POE>.
412
413 =head1 AUTHOR
414
415 Marc Lehmann <schmorp@schmorp.de>
416 http://anyevent.schmorp.de
417
418 =cut
419
420 1
421