ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/AnyEvent/lib/AnyEvent/Impl/POE.pm
Revision: 1.6
Committed: Fri Apr 25 02:09:33 2008 UTC (16 years, 2 months ago) by root
Branch: MAIN
Changes since 1.5: +3 -0 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 AnyEvent::Impl::POE - AnyEvent adaptor for POE
4
5 =head1 SYNOPSIS
6
7 use AnyEvent;
8 use POE;
9
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 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
22 Here are the details, and what it means to you if you want to be
23 interoperable with POE:
24
25 =over 4
26
27 =item Weird messages
28
29 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
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 immediately
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 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
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 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
64 =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 Of course, if POE reaps an unrelated child it will also output a message
90 for it. Very professional.
91
92 Therefore, AnyEvent has to resort to it's own SIGCHLD management, which
93 may interfere with POE.
94
95 =item Documentation Quality
96
97 At the time of this writing, POE was in its tenth year. Still, its
98 documentation is extremely lacking, making it impossible to implement
99 stuff as trivial as AnyEvent watchers without havign to resort to
100 undocumented behaviour or features.
101
102 For example, the POE::Kernel manpage has nice occurances of the word TODO
103 with an explanation of whats missing. Some other gems:
104
105 This allows many object methods to also be package methods.
106
107 This is nice, but since it doesn't document I<which> methods these are,
108 this is utterly useless information.
109
110 Terminal signals will kill sessions if they are not handled by a
111 "sig_handled"() call. The OS signals that usually kill or dump a
112 process are con‐ sidered terminal in POE, but they never trigger a
113 coredump. These are: HUP, INT, QUIT and TERM.
114
115 Although AnyEvent calls sig_handled, removing it has no apparent effects
116 on POE handling SIGINT.
117
118 Furthermore, since the Kernel keeps track of everything sessions do, it
119 knows when a session has run out of tasks to perform.
120
121 This is impossible - how does the kernel now that a session is no longer
122 watching for some (external) event (e.g. by some other session)? It
123 cannot, and therefore this is wrong.
124
125 It gets worse, though - the notion of "task" or "resource", although used
126 throughout the documentation, is not defined in a usable way. For example,
127 waiting for a timeout is considered to be a task, waiting for a signal is
128 not. The user is left guessing when waiting for an event counts as task
129 and when not.
130
131 One could go on endlessly - ten years, no usable docs.
132
133 It is likely that difefrences between documentation, or the one or two
134 things I had to guess, cause unanticipated problems with the backend.
135
136 =item Bad API
137
138 The POE API is extremely inconsistent - sometimes you have to pass a
139 session argument, sometimes it gets ignored, sometimes a session-specific
140 method must not use a session argument.
141
142 Sometimes registering a handler uses "eventname, parameter" (timeouts),
143 sometimes it is "parameter, eventname" (signals). There is little
144 consistency.
145
146 =back
147
148 On the good side, AnyEvent allows you to write your modules in a 99%
149 POE-compatible way (conflicting child watchers), without forcing your
150 module to use POE - it is still open to better event models, of which
151 there are plenty.
152
153 =cut
154
155 package AnyEvent::Impl::POE;
156
157 no warnings;
158 use strict;
159
160 use POE;
161
162 # have to do this to keep POE from spilling ugly messages
163 POE::Session->create (inline_states => { _start => sub { @_[KERNEL]->stop } });
164 POE::Kernel->run;
165
166 sub io {
167 my ($class, %arg) = @_;
168 my $poll = delete $arg{poll};
169 my $cb = delete $arg{cb};
170
171 # cygwin requires the fh mode to be matching, unix doesn't
172 my ($pee, $mode) = $poll eq "r" ? ("select_read" , "<")
173 : $poll eq "w" ? ("select_write", ">")
174 : Carp::croak "AnyEvent->io requires poll set to either 'r' or 'w'";
175
176 open my $fh, "$mode&" . fileno $arg{fh}
177 or die "cannot dup() filehandle: $!";
178
179 my $session = POE::Session->create (
180 inline_states => {
181 _start => sub {
182 $_[KERNEL]->$pee ($fh => "ready");
183 },
184 ready => sub {
185 $cb->();
186 },
187 stop => sub {
188 $_[KERNEL]->$pee ($fh);
189 },
190 },
191 );
192 bless \\$session, AnyEvent::Impl::POE::
193 }
194
195 sub timer {
196 my ($class, %arg) = @_;
197 my $after = delete $arg{after};
198 my $cb = delete $arg{cb};
199 my $session = POE::Session->create (
200 inline_states => {
201 _start => sub {
202 $_[KERNEL]->delay_set (timeout => $after);
203 },
204 timeout => sub {
205 $cb->();
206 },
207 stop => sub {
208 $_[KERNEL]->alarm_remove_all;
209 },
210 },
211 );
212 bless \\$session, AnyEvent::Impl::POE::
213 }
214
215 sub signal {
216 my ($class, %arg) = @_;
217 my $signal = delete $arg{signal};
218 my $cb = delete $arg{cb};
219 my $session = POE::Session->create (
220 inline_states => {
221 _start => sub {
222 $_[KERNEL]->sig ($signal => "catch");
223 $_[KERNEL]->refcount_increment ($_[SESSION]->ID => "poe");
224 },
225 catch => sub {
226 $cb->();
227 $_[KERNEL]->sig_handled;
228 },
229 stop => sub {
230 $_[KERNEL]->refcount_decrement ($_[SESSION]->ID => "poe");
231 $_[KERNEL]->sig ($signal);
232 },
233 },
234 );
235 bless \\$session, AnyEvent::Impl::POE::
236 }
237
238 sub DESTROY {
239 POE::Kernel->post (${${$_[0]}}, "stop");
240 }
241
242 sub one_event {
243 POE::Kernel->loop_do_timeslice;
244 }
245
246 1;
247
248 =head1 SEE ALSO
249
250 L<AnyEvent>, L<POE>.
251
252 =head1 AUTHOR
253
254 Marc Lehmann <schmorp@schmorp.de>
255 http://home.schmorp.de/
256
257 =cut
258