ev_prepare watcher awkwardness

Thilo Schulz thilo at tjps.eu
Sun Apr 19 19:29:29 CEST 2015


On Sunday 19 April 2015 12:31:22 Marc Lehmann wrote:
> They are being sorted by priority, just like any other event, which is the
> primary reason to do it this way: Not specialcasing them, but treating
> them the same.

Yep, I can see that is a good reason for ev_check watchers.
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.

Look, I am not necessarily pushing for a change in libev here, and telling
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. It is a 
pitfall that should be documented.

> That's why there is this thing called "documentation" - ev_prepare
> watchers are a very advanced concept (and have various limitations), and
> work if you use them for the documented purpose. If you want to use them
> for another purpose, it's not too much to ask to read the documentation
> more thoroughly.

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, but I am pretty sure that
these assertions of yours in our exchange here are missing:

- The nature and extent of ev_prepare watcher limitations
- Use of ev_prepare watchers not sanctioned by the libev author

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

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.

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

This is not quite as clear for ev_prepare watchers.

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

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

> Exactly - do you have any other use cases?
> > 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?
> You completely lost me now, first, what do timestamps have to do with
> anything, and second, ev_now gives you a real time timestamp of the same
> or higher precision as other real time stamps.

I have a second use case, apart from the module unloading.
I am writing a process automation system, with functionality somewhat akin to 
that of Matlab's System Generator. It has signal pathways with signals (having 
a value and an associated time stamp for when this value was generated) being 
passed on and processed in a fully event driven manner.
The signalling lines are connected via nodes that provide for various 
operations and a change in one signal value may result in changes in a graph 
of edges and nodes.

This may be a classic example for recursion, however, I want changes to 
progress one node at-a-time. So when computing the output values of one node, 
I need to defer further processing of the output values until all nodes 
connected to the input signal (the one triggering the change) have processed 
their respective output values.

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.

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.

> > 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 and then you're objecting to the 
statement contained in it. Do you really think that is helpful to this 

> > And using ev_timer really seems like abusing the timer watcher. Use
> > 0.000000001 as start time? That looks very hackish to me.
> 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.
Still, isn't there some unnecessary overhead connected to starting and 
stopping a timer for a simple deferral?

> > What I was writing was merely a suggestion, it would make it easier for
> > libev users to unravel recursion inside a watcher, which is basically
> > what I want to do here.
> 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 
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 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.

Best regards,
Thilo Schulz

More information about the libev mailing list