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