ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Impl/POE.pm
Revision: 1.31
Committed: Sat Aug 1 09:14:54 2009 UTC (14 years, 11 months ago) by root
Branch: MAIN
CVS Tags: rel-4_91, rel-5_112, rel-5_21, rel-5_22, rel-5_23, rel-5_24, rel-5_1, rel-5_0, rel-5_2, rel-5_201, rel-5_202, rel-5_111, rel-4_9, rel-5_01, rel-5_11, rel-5_12
Changes since 1.30: +1 -1 lines
Log Message:
4.9

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     AnyEvent::Impl::POE - AnyEvent adaptor for POE
4    
5 root 1.13 =encoding utf-8
6    
7 root 1.1 =head1 SYNOPSIS
8    
9 root 1.18 use AnyEvent;
10     use POE;
11    
12     # this module gets loaded automatically as required
13 root 1.1
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 root 1.22 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 root 1.1
26 root 1.3 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 root 1.2
30 root 1.3 Here are the details, and what it means to you if you want to be
31     interoperable with POE:
32 root 1.2
33     =over 4
34    
35     =item Weird messages
36    
37 root 1.4 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 root 1.2
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 root 1.5 ->run >> at AnyEvent loading time and stop the kernel immediately
48 root 1.2 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 root 1.16 The author of POE verified that this is indeed true, and has no plans to
53     change this.
54    
55 root 1.2 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 root 1.19 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 root 1.7 =item One POE session per Event
64 root 1.2
65     AnyEvent has to create one POE::Session per event watcher, which is
66 root 1.3 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 root 1.16 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 root 1.2
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 root 1.3 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 root 1.2
89 root 1.7 Of course, without the workaround, POE also prints ugly messages again
90     that say the program *might* be buggy.
91    
92 root 1.16 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 root 1.3 =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 root 1.11 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 root 1.16 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 root 1.12
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 root 1.3
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 root 1.25 after another (all else being equal), the callbacks might be called in
119 root 1.16 reverse order.
120 root 1.3
121     How one manages to even implement stuff that way escapes me.
122    
123     =item Child Watchers
124    
125 root 1.7 POE offers child watchers - which is a laudable thing, as few event loops
126 root 1.3 do. Unfortunately, they cannot even implement AnyEvent's simple child
127 root 1.25 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 root 1.6
144 root 1.7 As a workaround, AnyEvent::Impl::POE will take advantage of undocumented
145 root 1.25 behaviour in POE::Kernel to catch the status of all child processes, but
146     it cannot guarantee delivery.
147 root 1.7
148     How one manages to have such a glaring bug in an event loop after ten
149     years of development escapes me.
150 root 1.3
151 root 1.29 (There are more annoying bugs, for example, POE runs C<waitpid>
152     unconditionally on finalizing, so your program will hang until all child
153     processes have exited.)
154    
155 root 1.3 =item Documentation Quality
156    
157     At the time of this writing, POE was in its tenth year. Still, its
158     documentation is extremely lacking, making it impossible to implement
159 root 1.10 stuff as trivial as AnyEvent watchers without having to resort to
160 root 1.3 undocumented behaviour or features.
161    
162 root 1.16 For example, the POE::Kernel manpage has nine occurances of the word TODO
163 root 1.10 with an explanation of whats missing. In general, the POE manpages are
164     littered with comments like "section not yet written".
165    
166     Some other gems:
167 root 1.3
168     This allows many object methods to also be package methods.
169    
170     This is nice, but since it doesn't document I<which> methods these are,
171     this is utterly useless information.
172    
173     Terminal signals will kill sessions if they are not handled by a
174 root 1.10 "sig_handled"() call. The OS signals that usually kill or dump a
175     process are considered terminal in POE, but they never trigger a
176 root 1.3 coredump. These are: HUP, INT, QUIT and TERM.
177    
178 root 1.10 Although AnyEvent calls C<sig_handled>, removing it has no apparent
179     effects on POE handling SIGINT.
180 root 1.3
181 root 1.11 refcount_increment SESSION_ID, COUNTER_NAME
182    
183     Nowhere is explained which COUNTER_NAMEs are valid and which aren't - not
184     all scalars (or even strings) are valid counter names. Take your guess,
185 root 1.16 failure is of course completely silent. I found this out the hard way, as
186     the first name I came up with was silently ignored.
187 root 1.11
188     get_next_event_time() returns the time the next event is due, in a form
189     compatible with the UNIX time() function.
190    
191     And surely, one would hope that POE supports subsecond accuracy as
192     documented elsewhere, unlike the explanation above implies. Yet:
193    
194     POE::Kernel timers support subsecond accuracy, but don’t expect too
195     much here. Perl is not the right language for realtime programming.
196    
197     ... of course, Perl is not the right language to expect subsecond accuray
198     - the manpage author must hate Perl to spread so much FUD in so little
199     space. The Deliantra game server logs with 100µs-accuracy because Perl is
200     fast enough to require this, and is still able to deliver map updates with
201     little jitter at exactly the right time. It does not, however, use POE.
202    
203 root 1.3 Furthermore, since the Kernel keeps track of everything sessions do, it
204     knows when a session has run out of tasks to perform.
205    
206 root 1.11 This is impossible - how does the kernel know that a session is no longer
207 root 1.3 watching for some (external) event (e.g. by some other session)? It
208 root 1.10 cannot, and therefore this is wrong - but you would be hard pressed to
209     find out how to work around this and tell the kernel manually about such
210     events.
211 root 1.3
212     It gets worse, though - the notion of "task" or "resource", although used
213     throughout the documentation, is not defined in a usable way. For example,
214     waiting for a timeout is considered to be a task, waiting for a signal is
215 root 1.10 not (a session that only waits for a signal is considered finished and
216     gets removed). The user is left guessing when waiting for an event counts
217 root 1.15 as task and when not (in fact, the issue with signals is mentioned in
218     passing in a section about child watchers and directly contradicts earlier
219     parts in that document).
220 root 1.3
221 root 1.10 One could go on endlessly - ten years, no usable documentation.
222 root 1.3
223 root 1.8 It is likely that differences between documentation, or the one or two
224 root 1.10 things I had to guess, cause unanticipated problems with this adaptor.
225 root 1.3
226 root 1.16 =item Fragile and inconsistent API
227 root 1.3
228     The POE API is extremely inconsistent - sometimes you have to pass a
229     session argument, sometimes it gets ignored, sometimes a session-specific
230     method must not use a session argument.
231 root 1.2
232 root 1.10 Error handling is sub-standard as well: even for programming mistakes,
233     POE does not C<croak> but, in most cases, just sets C<$!> or simply does
234     nothing at all, leading to fragile programs.
235    
236     Sometimes registering a handler uses the "eventname, parameter" ordering
237     (timeouts), sometimes it is "parameter, eventname" (signals). There is
238     little consistency overall.
239 root 1.1
240 root 1.17 =item Lack of knowledge
241    
242 root 1.18 The IO::Poll event loop provides an alternative that theoretically
243     scales better than select().
244 root 1.17
245     The IO::Poll "event loop" (who in his right mind would call that an event
246     loop) of course scales about identically (sometimes it is a bit faster,
247     sometimes a bit slower) to select in theory, and also in practise, of
248     course, as both are O(n) in the number of file descriptors, which is
249     rather bad.
250    
251     This is just one place where it gets obvious how little the author of the
252     POE manpage understands.
253    
254 root 1.23 =item No idle events
255    
256     The POE-recommended workaround to this is apparently to use
257     C<fork>. Consequently, idle watchera will have to be emulated by AnyEvent.
258    
259 root 1.3 =back
260 root 1.1
261 root 1.9 On the good side, AnyEvent allows you to write your modules in a 100%
262     POE-compatible way (bug-for-bug compatible even), without forcing your
263 root 1.3 module to use POE - it is still open to better event models, of which
264     there are plenty.
265 root 1.1
266 root 1.28 Oh, and one other positive thing:
267    
268     RUNNING_IN_HELL
269    
270     POE knows about the nature of the beast!
271    
272 root 1.1 =cut
273    
274     package AnyEvent::Impl::POE;
275    
276 root 1.30 use AnyEvent (); BEGIN { AnyEvent::common_sense }
277 root 1.1 use POE;
278    
279 root 1.22 # if POE is already running
280     if (${ $poe_kernel->[POE::Kernel::KR_RUN] } && POE::Kernel::KR_RUN_CALLED) {
281     print STDERR <<EOF;
282 root 1.28
283     ***
284     *** POE is going to complain about:
285     ***
286     *** Sessions were started, but POE::Kernel's run() method was never...
287     ***
288     *** Try putting:
289     ***
290     *** use AnyEvent::Impl::POE;
291     ***
292     *** at the very top of your main program to suppress these spurious warnings.
293     ***
294    
295 root 1.22 EOF
296     } else {
297     # workaround to suppress noise
298     POE::Session->create (inline_states => { _start => sub { @_[KERNEL]->stop } });
299     POE::Kernel->run;
300     }
301 root 1.1
302     sub io {
303     my ($class, %arg) = @_;
304 root 1.2
305 root 1.26 # POE itself might do the right thing, but some POE backends don't,
306     # so do the safe thing, it's not as if this will slow us down
307     # any further *g*
308 root 1.21 my ($fh, $pee) = AnyEvent::_dupfh $arg{poll}, $arg{fh}, "select_read", "select_write";
309 root 1.2
310 root 1.27 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
311 root 1.19
312 root 1.1 my $session = POE::Session->create (
313     inline_states => {
314 root 1.26 _start => sub { $_[KERNEL]->$pee ($fh => "ready") },
315     ready => sub { $cb->() },
316     stop => sub { $_[KERNEL]->$pee ($fh) },
317 root 1.1 },
318     );
319 root 1.23 bless \\$session, "AnyEvent::Impl::POE"
320 root 1.1 }
321    
322     sub timer {
323     my ($class, %arg) = @_;
324 root 1.19
325 root 1.27 my $after = delete $arg{after};
326     my $ival = delete $arg{interval};
327     my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
328 root 1.19
329 root 1.1 my $session = POE::Session->create (
330     inline_states => {
331     _start => sub {
332     $_[KERNEL]->delay_set (timeout => $after);
333     },
334 root 1.20 timeout => $ival ? sub { $_[KERNEL]->delay_set (timeout => $ival); $cb->() } : $cb,
335 root 1.1 stop => sub {
336     $_[KERNEL]->alarm_remove_all;
337     },
338     },
339     );
340 root 1.23 bless \\$session, "AnyEvent::Impl::POE"
341 root 1.1 }
342    
343     sub signal {
344     my ($class, %arg) = @_;
345 root 1.31 my $signal = AnyEvent::Base::sig2name delete $arg{signal};
346 root 1.27 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
347 root 1.1 my $session = POE::Session->create (
348     inline_states => {
349     _start => sub {
350     $_[KERNEL]->sig ($signal => "catch");
351     $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
352     },
353     catch => sub {
354     $cb->();
355     $_[KERNEL]->sig_handled;
356     },
357     stop => sub {
358     $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
359     $_[KERNEL]->sig ($signal);
360     },
361     },
362     );
363 root 1.23 bless \\$session, "AnyEvent::Impl::POE"
364 root 1.1 }
365    
366 root 1.7 sub child {
367     my ($class, %arg) = @_;
368     my $pid = delete $arg{pid};
369 root 1.27 my $cb = delete $arg{cb}; my $cb = sub { &$cb }; # POE doesn't like callable objects
370 root 1.7 my $session = POE::Session->create (
371     inline_states => {
372     _start => sub {
373     $_[KERNEL]->sig (CHLD => "child");
374     $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
375     },
376     child => sub {
377     my ($rpid, $status) = @_[ARG1, ARG2];
378    
379     $cb->($rpid, $status) if $rpid == $pid || $pid == 0;
380     },
381     stop => sub {
382     $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
383     $_[KERNEL]->sig ("CHLD");
384     },
385     },
386     );
387 root 1.23 bless \\$session, "AnyEvent::Impl::POE"
388 root 1.7 }
389    
390 root 1.1 sub DESTROY {
391     POE::Kernel->post (${${$_[0]}}, "stop");
392     }
393    
394     sub one_event {
395     POE::Kernel->loop_do_timeslice;
396     }
397    
398 root 1.24 sub loop {
399     POE::Kernel->run;
400     }
401    
402 root 1.1 1;
403    
404     =head1 SEE ALSO
405    
406     L<AnyEvent>, L<POE>.
407    
408     =head1 AUTHOR
409    
410     Marc Lehmann <schmorp@schmorp.de>
411     http://home.schmorp.de/
412    
413     =cut
414