ev_prepare watcher awkwardness

Thilo Schulz thilo at tjps.eu
Mon Apr 20 02:33:01 CEST 2015


Hey Marc,

thanks for taking your time for replying! And even if you do display a certain 
abrasiveness while we're at it, and I do not see a few things your way, I 
really do learn alot by our exchange. That is greatly appreciated. Thank you.

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 
ev_check watchers.

      if (expect_false (preparecnt))
        {
          queue_events (EV_A_ (W *)prepares, preparecnt, EV_PREPARE);
          EV_INVOKE_PENDING;
        }

Further reading of the code suggests the only time you will have other events 
in the queue, is when a watcher from an EV_INVOKE_PENDING call prior to the 
queueing of prepares, with lower priority, does an ev_feed_event on a watcher 
with higher priority, right before the prepares are queued.

> That's ok, it's no a shame to have to ask the list, but you should listen
> to the replies and not stubbornly repeat the same things over and over.

Hey, you have written the documentation and I keep finding things in the doc 
after reading it a second or third time, but in this instance I fail.
I have also reread the section about ev_prepare watchers (which seems to be 
together with ev_check watchers)
I kindly ask you, could you point me to where you wrote about limitations on 
adding prepare watchers from other prepare watchers?

> ev_prepare watchers have their own manual section which documents their
> limitations.

In the doc I only see:
###
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.

> 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. Specifically, it was interesting to see that fork 
event watchers are queued, then EV_INVOKE_PENDING is called, then the prepare 
events, then again EV_INVOKE_PENDING.
Most of the documentation discusses how events are queued after the blocking 
syscall.
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. 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.

> Your problem certainly isn't to invoke something once per event loop
> iteration - your stated problem was to invoke another callback via your
> event loop, i.e. delayed execution of another callback, as soon as
> possible, without waiting for another iteration.
> 
> This is not what ev_prepare does.

Okay.

> Obviously, it is "intuitive" for you if these watchers have very different
> behaviour. I find it more intuitive (and rational) if they have the same
> behaviour, as they have in libev, and the same behaviour as any other
> watcher type in libev, namely triggering on their respective event, not on
> starting them.

Well, the doc says:

###
The rationale behind this is that you do not need to check for recursion in 
those watchers, i.e. the sequence will always be ev_prepare, blocking, 
ev_check so if you have one watcher of each kind they will always be called in 
pairs bracketing the blocking call.
###

"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 
watchers and they'll always get executed before the blocking" - which is true 
when they're started from any any other watcher callback than that of an 
ev_prepare watcher.

> Btw, it's dangerous to make up statements such as "any other watcher
> for that matter, will see its execution only after the next poll/select
> syscall", since they are not true - libev works as documented, and making
> your own rules will only get your surprised. Specifically, libev makes no
> guarantee like the one you stated, and current code doesn't ensure that it
> is true either, so it's likely that your code will break if you rely on
> your own advice.

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.

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

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

> > While all this is happening, even after the deferral, the timestamp
> > returned by ev_now() must not change, because my code relies on the
> > value returned by ev_now() as a reference time stamp for when a value is
> > generated on some node types. This practically rules out the use of
> > ev_timer and ev_idle.
> 
>    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.
And yes, there's no guarantee for ev_now() time not changing until the prepare 
watcher. You have convinced me :)

> What about time
> critical code, where real time timestamps are of importance?
> 
>    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. I 
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.
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.
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.

> > > Indeed, if you want the shortest possible delay, 0 would be simplest
> > > and most succinct.
> > 
> > Oh. For some reason, I thought that a timeout value of 0.0 was illegal
> > which it clearly is not. My bad.
> 
> Reading documentation really helps.

It was not for lack of reading the documentation that made me think it was 
illegal, I assure you. May have been some use case years before that didn't 
work as I expected for entirely different reasons. I don't remember. Human 
brains just work like that.

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

-- 
Best regards,
Thilo Schulz



More information about the libev mailing list