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