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