Can I synchronously relinquish control to the event loop?

Marc Lehmann schmorp at schmorp.de
Mon Feb 27 18:25:37 CET 2012


On Mon, Feb 27, 2012 at 11:01:54AM -0500, Fulko Hew <fulko.hew at gmail.com> wrote:
> And this seems to be causing networking difficulties because
> of either the amount of TX queuing, or RX queueing going on.

what kind of difficulties? at least ae::handle lets you queue as much as
you want - it might not be wise to do so (because ae::handle has to buffer
all the data in memory), but perfectly possible.

> So my first thought was, how can I, in my part 1 loop, allow
> the eventloop to detect and process transmits and receives,
> and when idle, let me (my current processing loop) continue?

I don't understand your problem well enough - for me, it sounds more as
if you want some event that tells you "now you can push more data", and
indeed, AE::Handle has such an event callback, called "on_drain" - the
on_drain callback is called whenever the (ae::handle) write buffer is
empty, and you can write more.

> My second thought was that somehow Coro can come to my
> rescue, but I don't understand how I can merge the two
> together, and what I need to do in my mainline and timer callback
> to get it to work.

If your problem is that you have this kind of code:

   while (read more file data...)
      push_write

and you want to "slow down" this loop to not be considerably faster then your
network connection can send data, then, with coro, you can do that:

   sub wait_for_on_drain {
      my ($handle) = @_;

      $handle->on_drain (Coro::rouse_cb);
      Coro::rouse_wait;
      $handle->on_drain (undef);
   }

I haven't tested the above code,, but what it does is basically this:

1. sets on_drain to a callback that we can wait for
   (a so-called rouse callback)
2. waits for the callback to be called
3. resets the on_drain handler again (this might not be needed
   if all you do is call this functino again, it should be cleared
   before you do other suff with your handle object though).

This is a less efficient but maybe more clear version using traditional
"signals" would be this:

   sub wait_for_on_drain {
      my ($handle) = @_;

      my $done = new Coro::Signal;
      $handle->on_drain (sub { $done->send });
      $done->wait;
      $handle->on_drain (undef);
   }

Your loop would then become something like:

   while (read more file data...)
      push_write
      wait_for_on_drain $handle

This is a pattern... most code that works like this

   $object->set_callback(sub { ... });

Can usually be written like this:

   $object->set_callback (Coro::rouse_cb);
   Coro::rouse_wait;
   ...

To invert the program flow from "call me back later" to "return later".

Finally, note that you cannot, with most event loops, and under most
circumstances, put the current thread to sleep when it executes inside the
event loop. That is, sleeping is a very bad idea in event loop callbacks,
because you just put the event loop to sleep which the event loop will
not like. That means that the above while loop must run as its own
independent thread:

   async {
      while (...
   };

Also, event processing will continue for everybody else with the above
solution, which is *probably* what you want anyway.

-- 
                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