ev_prepare watcher awkwardness

Marc Lehmann schmorp at schmorp.de
Mon Apr 20 00:21:19 CEST 2015


On Sun, Apr 19, 2015 at 07:29:29PM +0200, Thilo Schulz <thilo at tjps.eu> wrote:
> But ev_prepare watchers are, like you said, a special case, different
> from ev_check watchers, since ev_prepare do not queue with other watchers.

ev_prepare watchers do queue with other watchers - event handling is the
same for every watcher type.

> you "how libev should be done". I am telling you that I, as an end user not 
> intimately familiar with internal libev code, did not see this coming.

_Again_, that's what reading documentation is for.

> It is a pitfall that should be documented.

Since it is documented already (and you are aware of that), I really fail
to see your point.

> Well you say it's already in the documentation. I have read most of the
> documentation at least once, many parts of it multiple times.
> I don't have total recall, and maybe I missed it,

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.

> but I am pretty sure that these assertions of yours in our exchange here
> are missing:

You should ask yourself how you can be "pretty sure" and compledtely wrong
about it at the same time.

> - The nature and extent of ev_prepare watcher limitations

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

> - Use of ev_prepare watchers not sanctioned by the libev author

Uses not sanctioned are exhaustively documented in the libev documentation
(hint: the libev author doesn't sanction uses).

> so I am not sure how "reading the documentation more thoroughly" would have 
> helped.

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'd also like to remind you of this section of the documentation:
> "Abusing an ev_check watcher for its side-effect"
> which is, the way I see it, pretty much what I am trying to achieve, but with 
> the prepare instead of the check watcher.

I am not sure how you can claim that it is pretty much what you want to
achieve while at the same time complain that it doesn't do what you want:
ev_prepare watchers behave as described in that section, and that is
obviously not enough for your use case, therefore it's pretty much NOT
what you are trying to achieve.

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.

> > > So I think this behaviour should at least be documented in the section
> > > for prepare watchers.
> > 
> > It's not behaviour that is specific to prepare watchers.
> 
> Yes, that is true indeed!
> However, for watchers other than prepare, this behaviour will not cause the 
> blocking problem. ev_check watchers per documentation are only queued _after_ 
> the poll syscall, so it is intuitive that adding an ev_check watcher from 
> inside an ev_check watcher, or any other watcher for that matter, will see its 
> execution only after the next poll/select syscall.

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.

That way users of the library need to remember a lot less (and don't have
to deal with starvation or other problems your proposed behaviour can
cause).

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.

It's best not to make up guesses based about what you think you see and
stick to the documented interface, which is certainly enough to get your
problems solved.

> This is not quite as clear for ev_prepare watchers.

Well, that's not the fault of libev or it's documentation though.
> 
> > From your other mail (that I read afterwards), it very much looks as if
> > you are - ev_prepare watchers are not meant to delay actions, that's what
> > idle watchers or timers are for.
> 
> Actually, it is not delaying what I want. It is a side-effect of the 
> requirements I have and that I want to minimize. I want code to execute as 
> soon as possible after the return of a watcher, because I cannot have that 
> code running from the callstack of the watcher.

Then ev_prepare watchers are obviously the wrong type of watcher, because
these run neither soon, nor are they guaranteed to run at all. If a timer
isn't good enough for you, you can always queue your own custom event (and
use e.g. an ev_idle watcher).

> > > I stumbled over this problem when I wanted my program to shut down
> > > cleanly by stopping all watchers.  This way I get an indication when
> > > a watcher is still running even though it should not, by, well, the
> > > program just not quitting when it should :)
> > 
> > "This way" refers to what?
> 
> The manner, in which ev_run() is made to return: By making sure all watchers 
> are stopped. Not relevant to this discussion though.

Ok, let's drop it then.

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

> So, for my use-case, ev_prepare probably still is the way to go, but I'll just 
> have to implement my own queueing inside a prepare watcher.

Except ev_prepare doesn't ensure that ev_now doesn't change - again, this
is an assumption that is not documented, not true, and probably will bite
you later if you rely on it.

And I have no clue why you think you have to implement your own queueing,
but libev certainly supports you doing that if you don't want to use it's
own mechanisms...

> > > ev_idle makes no guarantee whatsoever how many event loop interations
> > > need to happen before it is first invoked, which is bad.
> > 
> > Actually, it's good, because that is what it is invented for.
> 
> Of course that is what it was invented for.
> You're quoting me completely out of context

I think this is getting rather disingenous now - I clearly didn't "quote
you out of context", you were talking about ev_now and timestamps:

   Yes, you are right for _this particular_ use case. And for those use cases
   where the precision of ev_now() is sufficient. 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.

I only say it once: if you want a reply from me, don't play games like
this.

> and then you're objecting to the statement contained in it. Do you
> really think that is helpful to this discussion?

The more this disucssion progresses, the more I get the impression that you
are not interested in a discussion or a solution to your problem.

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

> Still, isn't there some unnecessary overhead connected to starting and 
> stopping a timer for a simple deferral?

I don't know, define "unnecessary"? If you think the overhead is "too
much" (based on what?) then you can use one of the other methods I
mentioned.

> > Can you define "recursion inside a watcher" more clearly?
> 
> Well, returning to the graph example earlier, changing output values on a node 
> may trigger calls in other nodes that the input value has changed. A naive 
> implementation of this will lead to recursion, because those nodes may also 
> subsequently change their output values.
> By this I mean "recursion inside a watcher". From a user's perspective, I feel 
> that it would be cool if libev would give me some tools to break out of this 
> recursion.

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.

> Frankly, you can pick up on my suggestions or you cannot. I don't feel very 
> strongly about it. In the end, I will just do the queueing myself.

As you wish, nobody is forcing you to use libev to solve your problem.

> > As is the case with all your solutions: they are rather complicated, abuse
> > watchers for purposes they were not designed for
> 
> Yup. Hence my suggestion for a new watcher type.

Hmm, I must have overlooked this, but I didn't see a suggestion for a new
watcher type, nor, and this is relevant, any problem that a new watcher type
is required for.

As pointed out, you can solve all your problems using libev using existing
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