Redundant epoll_ctl with ev_io watcher

Nick Zavaritsky mejedi at
Tue Dec 15 15:04:45 CET 2015


Looks like I’ve let the ball fall on this one :)

Discussion summary: Each time ev_io_set is being called, libev will assume that this is potentially a new file descriptor.

Marc said it’s not ok to hack a watcher to disable this ev_io quirk when we know for sure that the descriptor is the old one.

A lightweight variant of ev_io_set to switch R,W,RW watcher modes was requested and denied.

I don’t intend to persist any further in hopes that Marc will change his mind :) Just a few observations though.

a) I believe that documentation permits moving ev_io watcher to a different event loop without doing ev_io_set first. The loop will mishandle the descriptor since ev_io_start in the previous event loop has cleared the special flag set by ev_io_set.

b) For a casual docs reader like myself it wasn’t obvious that multiple watchers for one descriptor should be used if switching between R,W,RW modes is desired. Probably a relevant example in the docs will help?


> On 07 Nov 2015, at 20:00, Marc Lehmann <schmorp at> wrote:
> On Fri, Nov 06, 2015 at 05:25:38PM +0300, Nick Zavaritsky <mejedi at> wrote:
>>> I don't quite see why you couldn't reuse the I/O watcher? I also use
>>> coroutines, and typically it is possible to reuse the watcher (for
>>> performance reasons, it is basically always faster to reuse watchers).
>> Technically, reusing is possible, though it will require a massive refactoring. And it will likely make the code somewhat more complicated. Probably it is the right thing to do anyway, but I would really like to research other options first.
> Are you sure it requires massive refactoring? While it might of course be
> true, a simple (dynamic) array with a busy bit should already do and catch
> most cases. Also, you already have to know whether your fd has changed or
> not, how do you do that? Is this implicit in the code using your coroutine
> layer?
> Also, you need to be clear on your goals - id you are ok with less
> performance and simpler code, your goal is already reached, apparently.
>>> You can do whatever you want as long as you don't complain later, however,
>>> libev doesn't really have a concept of "watcher flags" that is exposed.
>> So switching between (say) EV_READ and (EV_READ | EV_WRITE) mode is only *officially* possible via ev_io_set, right?
> Yes.
>> It would be great if it was possible to switch modes without triggering the code that works around the problem of reused file descriptors. Though at the first glance it seams that going from EV_READ to (EV_READ | EV_WRITE) requires a syscall anyway, that is not necessary the case, please consider the following situations:
> I am not convinved it would be so great. All your problems go away with a
> more efficient architecture: encouraging slower solutions is great why
> exactly?
>> (1) multiple watchers for the same fd in the same loop, it is still possible that the ‘union’ mode didn’t change after all;
>> (2) redundant R->RW->R mode switching before doing the next event loop iteration (I’ve actually asked about this use case on the mail list quite a while ago).
> For multiple watchers, this could indeed be optimised inside libev, maybe,
> possibly. Yet again, it solves a problem that doesn't happen with typical
> uses, and can be avoided by using a more efficient design for untypical
> uses.
>> I suggest adding a lighter version of ev_io_set that
> I think that would complicate the API for users, without adding much
> gain. I am not completely set in stone for this, but at the moment, your
> argument, which I essentially understand as "I have a suboptimal design
> to start with, so I want libev to waste some more efficiency and make it
> harder to use by others, so my suboptimal design works a bit better".
> It might be possible to implement some multiple-watcher optimisation with
> minimal overhead, which I might consider (and will look at when I have time),
> but in general, having yet another abstraction layer to deal with a problem
> that can be solved more efficiently already is not something I see as a
> worthy goal.
> And in your case, if you have some generic coroutine layer somewhere, it would
> seem especially reasonable to invest a bit of effort into making this as
> efficient as 
>> I would really like to reuse IO watchers if I could; however it is complicated — currently N coroutines can block waiting for the same object. We really depend on this feature. And we don’t know N beforehand.
> You could either add a waiting list to the watcher, or use some busy bit and
> flal back to a separate watcher if the "normal" one is in use. The later
> should be fine unless it is common for many coroutines to wait for the same
> fd, and if it is common, I relly question your design (what's the point of
> many coroutines to wait for read and write?). If it's a coroutine waiting
> for read and another for write, then you can have two watchers for that. Or a
> waiting list.
> And if it's just "in case" that you want to allow many coroutines to wait for
> the same fd, then it wouldn't be common.
> -- 
>                The choice of a       Deliantra, the free code+content MORPG
>      -----==-     _GNU_    
>      ----==-- _       generation
>      ---==---(_)__  __ ____  __      Marc Lehmann
>      --==---/ / _ \/ // /\ \/ /      schmorp at
>      -=====/_/_//_/\_,_/ /_/\_\

More information about the libev mailing list