use AnyEvent; use POE;
# this module gets loaded automatically as required
Unfortunately, POE isn't generic enough to implement a fully working AnyEvent backend: POE is too badly designed, too badly documented and too badly implemented.
Here are the details, and what it means to you if you want to be interoperable with POE:
Sessions were started, but POE::Kernel's run() method was never...
The message is correct, the question is why POE prints it in the first place in a correct program (this is not a singular case though).
The only way I found to work around this bug was to call "->run" at AnyEvent loading time and stop the kernel immediately again. Unfortunately, due to another design bug in POE, this cannot be done (by documented means at least) without throwing away events in the event queue.
The author of POE verified that this is indeed true, and has no plans to change this.
This means that you will either have to live with lost events or you have to make sure to load AnyEvent early enough (this is usually not that difficult in a main program, but hard in a module).
This is not just a problem with the way AnyEvent has to interact with POE, but is a principal issue with POEs lifetime management (namely that stopping the kernel stops sessions, but AnyEvent has no control over who and when the kernel starts or stops w.r.t. AnyEvent watcher creation/destruction).
From benchmark data it is not clear that session creation is that costly, though - the real inefficiencies with POE seem to come from other sources, such as event handling.
Of course, without the workaround, POE also prints ugly messages again that say the program *might* be buggy.
While this is not good to performance, at least regarding speed, with a modern Linux kernel, the overhead is actually quite small.
In addition, most timer functions in POE want an absoltue timestamp, which is hard to create if all you have is a relative time and no function to return the ``current time''.
And of course POE doesn't handle time jumps at all (not even when using an event loop that happens to do that, such as EV, as it does its own unoptimised timer management).
AnyEvent works around the unavailability of the current time using relative timers exclusively, in the hope that POE gets it right at least internally.
How one manages to even implement stuff that way escapes me.
Of course, if POE reaps an unrelated child it will also output a message for it that you cannot suppress (which shouldn't be too surprising at this point). Very professional.
As a workaround, AnyEvent::Impl::POE will take advantage of undocumented behaviour in POE::Kernel to catch the status of all child processes.
Unfortunately, POE's child handling is racy: if the child exits before the handler is created (which is impossible to avoid in general), one has to wait for another event to occur, which can take an indefinite amount of time (apparently POE does a busy-waiting loop every second, but this is not guarenteed or documented, so in practise child status events can be delayed for up to a second ``only'' at least in the current version).
How one manages to have such a glaring bug in an event loop after ten years of development escapes me.
For example, the POE::Kernel manpage has nine occurances of the word TODO with an explanation of whats missing. In general, the POE manpages are littered with comments like ``section not yet written''.
Some other gems:
This allows many object methods to also be package methods.
This is nice, but since it doesn't document which methods these are, this is utterly useless information.
Terminal signals will kill sessions if they are not handled by a "sig_handled"() call. The OS signals that usually kill or dump a process are considered terminal in POE, but they never trigger a coredump. These are: HUP, INT, QUIT and TERM.
Although AnyEvent calls "sig_handled", removing it has no apparent effects on POE handling SIGINT.
refcount_increment SESSION_ID, COUNTER_NAME
Nowhere is explained which COUNTER_NAMEs are valid and which aren't - not all scalars (or even strings) are valid counter names. Take your guess, failure is of course completely silent. I found this out the hard way, as the first name I came up with was silently ignored.
get_next_event_time() returns the time the next event is due, in a form compatible with the UNIX time() function.
And surely, one would hope that POE supports subsecond accuracy as documented elsewhere, unlike the explanation above implies. Yet:
POE::Kernel timers support subsecond accuracy, but don’t expect too much here. Perl is not the right language for realtime programming.
... of course, Perl is not the right language to expect subsecond accuray - the manpage author must hate Perl to spread so much FUD in so little space. The Deliantra game server logs with 100µs-accuracy because Perl is fast enough to require this, and is still able to deliver map updates with little jitter at exactly the right time. It does not, however, use POE.
Furthermore, since the Kernel keeps track of everything sessions do, it knows when a session has run out of tasks to perform.
This is impossible - how does the kernel know that a session is no longer watching for some (external) event (e.g. by some other session)? It cannot, and therefore this is wrong - but you would be hard pressed to find out how to work around this and tell the kernel manually about such events.
It gets worse, though - the notion of ``task'' or ``resource'', although used throughout the documentation, is not defined in a usable way. For example, waiting for a timeout is considered to be a task, waiting for a signal is not (a session that only waits for a signal is considered finished and gets removed). The user is left guessing when waiting for an event counts as task and when not (in fact, the issue with signals is mentioned in passing in a section about child watchers and directly contradicts earlier parts in that document).
One could go on endlessly - ten years, no usable documentation.
It is likely that differences between documentation, or the one or two things I had to guess, cause unanticipated problems with this adaptor.
Error handling is sub-standard as well: even for programming mistakes, POE does not "croak" but, in most cases, just sets $! or simply does nothing at all, leading to fragile programs.
Sometimes registering a handler uses the ``eventname, parameter'' ordering (timeouts), sometimes it is ``parameter, eventname'' (signals). There is little consistency overall.
On the good side, AnyEvent allows you to write your modules in a 100% POE-compatible way (bug-for-bug compatible even), without forcing your module to use POE - it is still open to better event models, of which there are plenty.
Marc Lehmann <email@example.com> http://home.schmorp.de/