ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Impl/POE.pm
Revision: 1.24
Committed: Sat Jun 20 07:14:35 2009 UTC (15 years ago) by root
Branch: MAIN
CVS Tags: rel-4_412, rel-4_42
Changes since 1.23: +4 -0 lines
Log Message:
*** empty log message ***

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