ev_prepare watcher awkwardness

Marc Lehmann schmorp at schmorp.de
Mon Apr 20 17:25:54 CEST 2015


On Mon, Apr 20, 2015 at 02:33:01AM +0200, Thilo Schulz <thilo at tjps.eu> wrote:
> On Monday 20 April 2015 00:21:19 Marc Lehmann wrote:
> > ev_prepare watchers do queue with other watchers - event handling is the
> > same for every watcher type.
> 
> Yes, I noticed that. If I read your code correctly, ev_prepare watchers will 
> in fact almost never be queued together with other watchers, as opposed to 

Nobody claims it is often, nor would it be relevant - the point is that
they do queue like and together with other watchers whenever this is
required.

> I kindly ask you, could you point me to where you wrote about limitations on 
> adding prepare watchers from other prepare watchers?

First of all, there are no limitations on adding prepare watchers from
other prepare watchers. What you talked about is the semantics of doing
that, and this is described in detail in the ev_run documentation.

> Also, ev_check watchers (and ev_prepare watchers, too) should not activate 
> ("feed") events into libev. While libev fully supports this, they might get 
> executed before other ev_check watchers did their job. As ev_check watchers 
> are often used to embed other (non-libev) event loops those other event loops 
> might be in an unusable state until their ev_check watcher ran (always remind 
> yourself to coexist peacefully with others).
> ###
> 
> It says "might get executed before other ev_check watchers"... I see nothing 
> to indicate what happens when you start a prepare watcher from another prepare 
> watcher.

Because there is nothing special about it - adding an I/O watcher or a
timer will also not magically invoke it before the next poll, only when
their respective event occured.

For the person who really wants to know the details, you can look up ev_run,
which explains in greta detail what ev_run does.

And if you need to know more, you can read the source code.

None of that is required to solve your problem, so this is all academic.

> > That's ok, reading documentation can be hard, especially one one uses code
> > in a way that it wasn't meant to be used for. Still, the documentation
> > thoroughly describes how events are processed.
> 
> I only really started understanding how libev queues watcher events after 
> actually reading libev code.

You mention this as if it would somehow be a problem, or unexpected. To
*really* understand libev, reading its source code might well be required.

The important point to take home is that, even though you might understand
how libev does it, that doesn't mean you can rely on the undocumented
bits.

Yes, there might be this internal function that might seem to do exactly
what you want, and you can clal it, but it might not even exist in a
future version.

> event watchers are queued, then EV_INVOKE_PENDING is called, then the prepare 
> events, then again EV_INVOKE_PENDING.

That's true (and documented), but relying on it would be a bug, therefore,
while we can disucss internals, you need to understand that relying on
such internals is a bug in your code.

> Your documentation is actually a good read. Maybe you did describe all of the 
> aspects that I've written about just now in this paragraph, in a completely 
> different section.

Yes, and I don't expect you to memorize everything, understand everything,
and neither do I expect the documentationo to be always correct or complete.

However, when you try to speak authoritatively about libevs behaviour and
what works and what doesn't, you need to spent a lot more effort than if
you just want to ask a clarifying question.

> Oftentimes however these things hide in one sentence that 
> are easy to miss and one doesn't notice them until one has read the doc the 
> umpeenth time.

Which is not a problem, since people who use the documented API in the
described way will not need this deep understanding.

There are a _lot_ of subtle details in libev that are not even required to
create a nominally working event library and understanding of is not required to
use it, but which might avoid surprises. For example, one such detail is that
starting a watcher will not lead to starvation.

So, while we can move from the user-level to the implementation level and
happily discuss implementation strategies or rationale, you (and other
people reading this) should be aware that these are internals that are
free to study and free to exploit, but if stuff breaks, it's all your
problem.

> "Bracketing" is the key word. Some libev user as naive as me would think: 
> "hey, the blocking call has not been called yet, so I can start ev_prepare 

The documentation explicitly says this is about recursion and explicitly
mentions ev_io and ev_timer though, not starting more ev_prepare and
ev_check watchers, which makes no sense for the stated purpose.

It is also noteworthy that in the usage example you brought up, the calls
are still bracketed as well (since you don't have an ev_check watcher that
could rely on it).

So, no, a naive libev user simply wouldn't think that, because naive usage of
ev_prepare actually works.

What doesn't work is when you try to use ev_prepare for a completely
different usage, for which it isn't designed and for which it wouldn't
work anyway, because even *if* you start another ev_prepare watcher inside
an ev_prepare watcher callback it *will* be called before the process
blocks.

> I should have exempted some special watchers, like the fork watcher, and 
> watchers that were added to pending watchers via ev_feed_event().
> At least for fd watchers I think my assumption will always hold true. At this 
> time timeout callbacks are also only called after the blocking syscall. But 
> yes, you're right. There's no guarantee for it in the doc.

And you can legally (and in some cases sensibly) feed events to fd (io)
watchers as well, so your assumption is not true for io watchers either. or
timers.

> > > This is not quite as clear for ev_prepare watchers.
> > 
> > Well, that's not the fault of libev or it's documentation though.
> 
> Well, here I disagree, obviously :)

Documentation can only make it *possible* for you to understand things, it
cannot *guarantee* it.

And in this case, the libev documentation is rather detailed, not to mention
that, as a free software library, you cna go to whatever level of detail you
need by studying the source code.

No, nobody can guarantee that you will have a clear understanding afterwards.

Again, the main point is that understanding these details is not required
unless you try to do funny things, for example, use ev_prepare as a delay,
which it isn't.

If you want to do funny things, you can be reasonably expected to do more
reserarch and even sit down and study the sources. Because if you don't
and things break, it's all your fault.

> > Then ev_prepare watchers are obviously the wrong type of watcher, because
> > these run neither soon, nor are they guaranteed to run at all.
> 
> Not guaranteed to run at all? How come?

Events can be handled without blocking, timers might still work while
ev_prepare won't. Embedded loops are another example.

> >    ev_tstamp event_time = ev_now ();
> > 
> >    event_time will not change unless you need it to - problem solved.
> 
> event_time will have to be preserved and communicated over multiple function 
> calls on the off-chance some other function might need it. Kind of a pain but 
> doable.

Easy to store it in a watcher.

In any case, this is a requirement that is very uncommon, and libev cannot
offer a standard way to do this, as this depends very much on your code, so
it quite obviously has to be put into your code.

What can be expected of libev is that it provides you all the tools to
schedule timers the way you need it, which is not what other event loops
provide (it's impossible to make timeouts relative to a specific time in
libevent for example, and libevent isn't especially bad here, this is
actually quite common for event libraries).

So you are kind of complaining that libev let's you do what you want to do.

> >    ev_idle makes no guarantee whatsoever how many event loop interations
> >    need to happen before it is first invoked, which is bad.
> 
> Time critical code means to me: Execution shall happen as fast as possible.

That might well be a job for an ev_idle watcher. "as fast as possible"
depends on your problem domain and might well mean "after all other events
have been handled".

Again, libev gives you options here that you can choose from, for many
concrete instantiations of "as fast as possible", even downto "as fast as
possible, no matter what" which would boild down to simply doing the work
in your callback.

> wasn't really clear with the "real time timestamps" either: It means the 
> defer-executed code needs to generate a real time as returned by ev_time() for 
> further processing, as soon as possible.

well, ev_time () is the perfect candidate to get a real time stamp like
the one returned by ev_time - just call it and one will be given?

the resolution might not be to your liking, in which case you might want
to use another source for timing, but that would be out of scoep for
libev.

> I understand now ev_prepare is not the right watcher for this. But ev_idle 
> definitely is even worse, because here we cannot be sure when that watcher is 
> ever going to be called.

If you use an idle watcher and feed it a custom event, as has been
mentioned multiple times, it would be called long before your prepare
watcher. No need to even start it. And if it has higher priority than
the currently executed watcher, it will even be the very next callback
invocation, a mere dozen cycles or so later.

(It also works with ev_prepare watchers).

The point to take home is that ev_idle *might* be the right watcher, as
*might* be ev_timer, or maybe another watcher - it all depends on the details
of your problem.

For example, ev_idle might be a good candidate to unload a module - if
your program is very busy, it would not waste cycles on unloading.

> In this context, I was saying for this time critical code, starting an ev_idle 
> watcher and waiting for an idle event is really bad.

And I still disagree - you didn't make a very elaborate example then
or now, and saying "ev_idle" is bad for this would be oversimplifying
when ev_idle might be the right thing to do in time critical cases like
this. Or maybe not...

> > Well, as I wrote, you can queue an event for later. Or use a
> > timer. Depending on your needs and correctness. timers have the advantage
> > that they don't starve the event loop.
> 
> Right, ev_feed_event(). I assume it will work if I use feed event on any 
> watcher with the same priority as the watcher being currently executed. 
> Correct?

It will work on any valid watcher, whether started or not, regardless
of priority. Priority only orders watchers, it doesn't stop them from
working (see the documentation of ev_priority).

These events will be executed during the current or next ev_invoke_pending
call, so typically really soon, and typically long before eny ev_prepare
watchers.

-- 
                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 libev mailing list