Redundant epoll_ctl with ev_io watcher
mejedi at gmail.com
Fri Nov 6 15:25:38 CET 2015
Thank you very much for your help.
> 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.
>> How wrong would it be to bypass ev_io_set when we know for sure that the descriptor didn’t get reused in the meantime?
> It is a complex decision that might differ between event loops, which is why
> it's hard to do that from user code, and there is no other interface provided
> than the existing one (same watcher).
Well, the section about ’the special problem of disappearing file descriptors’ is really well written and it is really helpful; I think I do understand the problem and the libev solution. I also understand what does the word ‘abstraction’ mean :)
>> Can we change watcher flags directly provided that it is inactive?
> 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?
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:
(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).
It looks like the libev code is already optimising for these cases unless EV__IOFDSET flag was set.
I suggest adding a lighter version of ev_io_set that
(a) updates mode only;
(b) has to be called with an already initialized watcher (and since only mode is passed it is unlikely that anyone will ever violate this restriction);
(c) preserves EV__IOFDSET bit if it was set in flags;
(d) doesn’t set EV__IOFDSET like ev_io_set does.
> Since it should be close to trivial for you to reuse existing I/O watchers
> (just put them into an array indexed by fd for example, or you could
> attach them to your filehandle abstraction if you have one), at this
> moment, this sounds like the preferable solution, and might even be faster
> than what you do now (what you try at the moment sounds as if you decided
> on a suboptimal way to do things and try to force it through).
Well, we were doing the things in a suboptimal way for quite a long time now, so changes require effort. I’m not forcing anything through, just considering the available options and asking for advice )
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.
The suboptimal solution that comes to my mind is to add a X flag to filehandle abstraction. X is initially set. Initialising io watcher from the filehandle will set EV__IOFDSET iff X is set. Finally it resets X.
More information about the libev