ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Impl/POE.pm
Revision: 1.4
Committed: Fri Apr 25 01:45:49 2008 UTC (16 years, 2 months ago) by root
Branch: MAIN
Changes since 1.3: +3 -2 lines
Log Message:
piss

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3     AnyEvent::Impl::POE - AnyEvent adaptor for POE
4    
5     =head1 SYNOPSIS
6    
7     use AnyEvent;
8 root 1.2 use POE;
9 root 1.1
10     # this module gets loaded automatically as required
11    
12     =head1 DESCRIPTION
13    
14     This module provides transparent support for AnyEvent. You don't have to
15     do anything to make POE work with AnyEvent except by loading POE before
16     creating the first AnyEvent watcher.
17    
18 root 1.3 Unfortunately, POE isn't generic enough to implement a fully working
19     AnyEvent backend: POE is too badly designed, too badly documented and too
20     badly implemented.
21 root 1.2
22 root 1.3 Here are the details, and what it means to you if you want to be
23     interoperable with POE:
24 root 1.2
25     =over 4
26    
27     =item Weird messages
28    
29 root 1.4 If you only use C<run_one_timeslice> (as AnyEvent has to for it's
30     condition variables), POE will print an ugly, unsupressable, message at
31     program exit:
32 root 1.2
33     Sessions were started, but POE::Kernel's run() method was never...
34    
35     The message is correct, the question is why POE prints it in the first
36     place in a correct program (this is not a singular case though).
37    
38     The only way I found to work around this bug was to call C<<
39     ->run >> at anyevent loading time and stop the kernel imemdiately
40     again. Unfortunately, due to another design bug in POE, this cannot be
41     done (by documented means at least) without throwing away events in the
42     event queue.
43    
44     This means that you will either have to live with lost events or you have
45     to make sure to load AnyEvent early enough (this is usually not that
46     difficult in a main program, but hard in a module).
47    
48     =item One session per Event
49    
50     AnyEvent has to create one POE::Session per event watcher, which is
51 root 1.3 immensely slow and makes watchers very large. The reason for this is
52     lacking lifetime management (mostly undocumented, too). Without one
53     session/watcher it is not possible to easily keep the kernel from running
54     endlessly.
55 root 1.2
56     =item One watcher per fd/event combo
57    
58     POE, of course, suffers from the same bug as Tk and some other badly
59     designed event models in that it doesn't support multiple watchers per
60 root 1.3 fd/poll combo. The workaround is the same as with Tk: AnyEvent::Impl::POE
61     creates a separate file descriptor to hand to POE, which isn't fast and
62     certainly not nice to your resources.
63 root 1.2
64 root 1.3 =item Timing Deficiencies
65    
66     POE manages to not have a function that returns the current time. This is
67     extremely problematic, as POE can use different time functions, which can
68     differ by more than a second. In addition, most timer functions in POE
69     want an absoltue timestamp, which is hard to create if all you have is a
70     relative time and no function to return the "current time".
71    
72     AnyEvent works around this by using relative timer fucntions, in the hope
73     that POE gets it right at least internally.
74    
75     =item Event Non-Ordering
76    
77     POE cannot guarentee the order of callback invocation for timers, and
78     usually gets it wrong. That is, if you have two timers, one timing out
79     after another, the callbacks might be called in reverse order.
80    
81     How one manages to even implement stuff that way escapes me.
82    
83     =item Child Watchers
84    
85     POE offers child watchers - which is a laudable thing, few event loops
86     do. Unfortunately, they cannot even implement AnyEvent's simple child
87     watchers: they are not generic enough.
88    
89     Therefore, AnyEvent has to resort to it's own SIGCHLD management, which
90     may interfere with POE.
91    
92     =item Documentation Quality
93    
94     At the time of this writing, POE was in its tenth year. Still, its
95     documentation is extremely lacking, making it impossible to implement
96     stuff as trivial as AnyEvent watchers without havign to resort to
97     undocumented behaviour or features.
98    
99     For example, the POE::Kernel manpage has nice occurances of the word TODO
100     with an explanation of whats missing. Some other gems:
101    
102     This allows many object methods to also be package methods.
103    
104     This is nice, but since it doesn't document I<which> methods these are,
105     this is utterly useless information.
106    
107     Terminal signals will kill sessions if they are not handled by a
108     "sig_handled"() call. The OS signals that usually kill or dump a
109     process are con‐ sidered terminal in POE, but they never trigger a
110     coredump. These are: HUP, INT, QUIT and TERM.
111    
112     Although AnyEvent calls sig_handled, removing it has no apparent effects
113     on POE handling SIGINT.
114    
115     Furthermore, since the Kernel keeps track of everything sessions do, it
116     knows when a session has run out of tasks to perform.
117    
118     This is impossible - how does the kernel now that a session is no longer
119     watching for some (external) event (e.g. by some other session)? It
120     cannot, and therefore this is wrong.
121    
122     It gets worse, though - the notion of "task" or "resource", although used
123     throughout the documentation, is not defined in a usable way. For example,
124     waiting for a timeout is considered to be a task, waiting for a signal is
125     not. The user is left guessing when waiting for an event counts as task
126     and when not.
127    
128     One could go on endlessly - ten years, no usable docs.
129    
130     It is likely that difefrences between documentation, or the one or two
131     things I had to guess, cause unanticipated problems with the backend.
132    
133     =item Bad API
134    
135     The POE API is extremely inconsistent - sometimes you have to pass a
136     session argument, sometimes it gets ignored, sometimes a session-specific
137     method must not use a session argument.
138 root 1.2
139 root 1.3 Sometimes registering a handler uses "eventname, parameter" (timeouts),
140     sometimes it is "parameter, eventname" (signals). There is little
141     consistency.
142 root 1.1
143 root 1.3 =back
144 root 1.1
145 root 1.3 On the good side, AnyEvent allows you to write your modules in a 99%
146     POE-compatible way (conflicting child watchers), without forcing your
147     module to use POE - it is still open to better event models, of which
148     there are plenty.
149 root 1.1
150     =cut
151    
152     package AnyEvent::Impl::POE;
153    
154     no warnings;
155     use strict;
156    
157     use POE;
158    
159     # have to do this to keep POE from spilling ugly messages
160     POE::Session->create (inline_states => { _start => sub { @_[KERNEL]->stop } });
161     POE::Kernel->run;
162    
163     sub io {
164     my ($class, %arg) = @_;
165     my $poll = delete $arg{poll};
166     my $cb = delete $arg{cb};
167 root 1.2
168     # cygwin requires the fh mode to be matching, unix doesn't
169     my ($pee, $mode) = $poll eq "r" ? ("select_read" , "<")
170     : $poll eq "w" ? ("select_write", ">")
171     : Carp::croak "AnyEvent->io requires poll set to either 'r' or 'w'";
172    
173     open my $fh, "$mode&" . fileno $arg{fh}
174     or die "cannot dup() filehandle: $!";
175    
176 root 1.1 my $session = POE::Session->create (
177     inline_states => {
178     _start => sub {
179 root 1.2 $_[KERNEL]->$pee ($fh => "ready");
180 root 1.1 },
181     ready => sub {
182     $cb->();
183     },
184     stop => sub {
185 root 1.2 $_[KERNEL]->$pee ($fh);
186 root 1.1 },
187     },
188     );
189     bless \\$session, AnyEvent::Impl::POE::
190     }
191    
192     sub timer {
193     my ($class, %arg) = @_;
194     my $after = delete $arg{after};
195     my $cb = delete $arg{cb};
196     my $session = POE::Session->create (
197     inline_states => {
198     _start => sub {
199     $_[KERNEL]->delay_set (timeout => $after);
200     },
201     timeout => sub {
202     $cb->();
203     },
204     stop => sub {
205     $_[KERNEL]->alarm_remove_all;
206     },
207     },
208     );
209     bless \\$session, AnyEvent::Impl::POE::
210     }
211    
212     sub signal {
213     my ($class, %arg) = @_;
214     my $signal = delete $arg{signal};
215     my $cb = delete $arg{cb};
216     my $session = POE::Session->create (
217     inline_states => {
218     _start => sub {
219     $_[KERNEL]->sig ($signal => "catch");
220     $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
221     },
222     catch => sub {
223     $cb->();
224     $_[KERNEL]->sig_handled;
225     },
226     stop => sub {
227     $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
228     $_[KERNEL]->sig ($signal);
229     },
230     },
231     );
232     bless \\$session, AnyEvent::Impl::POE::
233     }
234    
235     sub DESTROY {
236     POE::Kernel->post (${${$_[0]}}, "stop");
237     }
238    
239     sub one_event {
240     POE::Kernel->loop_do_timeslice;
241     }
242    
243     1;
244    
245     =head1 SEE ALSO
246    
247     L<AnyEvent>, L<POE>.
248    
249     =head1 AUTHOR
250    
251     Marc Lehmann <schmorp@schmorp.de>
252     http://home.schmorp.de/
253    
254     =cut
255