Where's AnyEvent's run_one_timeslice?

Marc Lehmann schmorp at schmorp.de
Fri Aug 7 15:46:15 CEST 2015


This answer is a bit long - it's basically an expanded version of the
relevant parts of the AnyEvent documentation.

On Thu, Aug 06, 2015 at 09:32:45PM -0700, Mike Schilli <mike.schilli at gmail.com> wrote:
> POE solves the problem by inverting control, by calling back into the
> event loop until a condition is met, like:

Causing recursion problems that are not controllable in general from a
module.

> Since I can't block in a callback in AnyEvent

This is not a limitation in AnyEvent, AnyEvent merely tries to diagnose
it. When you recurse into the event loop you need to be extremely careful
about it, and in general, you can't do this when you don't control all
code in your program (e.g. when its used in modules). When you write a
module, you don't control what else is loaded, and you don't control the
main program either.

Since AnyEvent specifically was written to support modules, it will not
allow this to happen (you cna still make it happen with little problem
though, but not through AnyEvent). If it did, then it would be possible
to easily write modules that mysteriously break other modules.

> (via a condvar or even via Coro),

You certainly can block on a condvar in Coro in a callback, as long as
you still run the event loop somewhere else. This isn't supported by most
event loops, though.

> is there a similar way I can call back into the event loop to run a
> slice and check back regularly if my condition is met, which is when I
> exit my blocking function?

No, and that is by design. If it hurts, you are doing it wrong. If you do
it wrong, you need another solution. You could try calling the underlying
event loop functions directly, but that will break the promise that
AnyEvent gives for module users, namely that things work if you follow its
rules.

That means that your module, even though it might use AnyEvent, isn't a
proper anyevent user - people who use your module can randomly experience
errors, and it will not play nice wiht other modules or programs, because
ultimately, it's not event based.

I understand that you would probably not get into this situation if you
hadn't gotten legacy code/design to care for. Your best bet is to make
your blocking function truly blocking, which should work fine and would
expose the synchronous nature of that function.

The underlying reason is that AnyEvent is not an event loop, nor does it
try to be one. This brings disadvantages (you can't add modal behaviour),
but also advantages, namely that modules using AnyEvent really are event
based and don't rely on race conditions to work.

Specifically, if there was an AnyEvent way to do have this modal
behaviour, and you used it in your module, then your module would be the
only module in a process that can do that. This is exactly why people
didn't use event loops in cpan modules much, because each such module in
effect creates it's own event loop, and you can only run one of them. The
purpose of AnyEvent is to allow modules to use an event loop without
monopolizing the process.

For example, Coro::AnyEvent also needs a way, for historical reasons
(but also for immense convenience), to run the event loop. However,
Coro::AnyEvent is one of the few cases where this would actually be OK to do,
because while introducing the problem of modality, it also implements a
solution to it at the same time.

For such modules, it would be convenient to have these functions - Coro
currently does take a speed hit and some inconvenience because AnyEvent
doesn't have those functions.

But those modules are extremely rare (I only know of Coro::AnyEvent).

A negative example is IO::Async::Loop::AnyEvent, which calls undocumented
functions to block, abusing AnyEvent as a cheap backend for itself, but
also making it impossible to use other event loops. It forces the bug, but
does nothing to fix it. It could be fixed, but the author doesn't care for
non-IO::Async users and instead runs around and asks for my modules to be
removed form CPAN instead.

Thus the trade-off for AnyEvent was to either provide these functions and
make it easy to break other people's code, or to not provide them, so
people (like you right now) run into a dead end and wonder, "hmm, where
is this function, how do I do it", and the answer is, you can't do it,
because you would break AnyEvent for other people with it.

So your options are either to make this function truly blocking (because
it is - it cannot coexist with another event loop), or to call the
underlying event loop functions yourself, thereby indicating that this
function is not usable in event-based programs.

Another way to view this is that calling your function (or callback)
essentially takes over the process and forms it's own event loop. This
is not ok in a module using AnyEvent, unless it represents the main
program. If it represents the main program, then it knows what event loop
it uses and can simply call it's one_event function.

In your case, the event loop probably was POE - your module only works
with POE, so there is no big issue in recursing into it (coding-wise),
except that other modules then can't do that anymore, but who cares,
recursing is rare anyways.

When trying to switch to AnyEvent, this fails, as AnyEvent isn't the
event loop anymore, and it's hard to control the event loop through it
(by design only), so it's no help in implementing blocking behaviour that
blocks out other similar modules - AnyEvent using modules either work
together, or they have to resort to diretc event loop calling.

-- 
                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_              http://www.deliantra.net
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp at schmorp.de
      -=====/_/_//_/\_,_/ /_/\_\



More information about the anyevent mailing list