I'm writing a coroutine wrapper for sockets as a demo for coroutine use cases and I'm struggling a bit with how to use epoll safely (without introducing race conditions).
I have already figured out that I have to use the edge mode EPOLLET
with EPOLLONESHOT
. But now I'm not sure when I should be rearming the socket.
Should I rearm before calling a non-blocking operation or after? I want to make sure that I neither miss the event nor receive a phantom one.
// epoll & socket setup
int ret;
ret = epoll_ctl(epoll_, EPOLL_CTL_MOD, ...); // rearm here
//...
ret = read(...);
//...
ret = epoll_ctl(epoll_, EPOL_CTL_MOD, ...); // or here?
int ret = epoll_wait(...);
CodePudding user response:
Should I rearm before calling a non-blocking operation or after?
Technically, after, but it's not as simple as that.
Regardless of EPOLLONESHOT
, once you receive an edge-triggered event signalling read-readiness of a given file descriptor, you must consider that FD to continue to be ready until a read()
on it fails with errno
set to EAGAIN
(and therefore the file must be in non-blocking mode). Over the course of those reads, it may be the case that you read all remaining bytes with one read()
, but then more arrive before the next. In that case, if the FD is still armed then a new event for it will be queued (or merged with another event for that FD, as appropriate). This is the case that could result in you receiving an event when in fact the FD is not any longer ready.
You should consider just accepting those "phantom" events. Since your file will be in non-blocking mode, they will not cause unwanted stalls, just a little extra work. And your code will be simpler. But if you do use EPOLLONESHOT
to avoid receiving phantom events, then you must not re-arm the FD before you determine it to be unready (via a read
failing with EAGAIN
), else you defeat the purpose.
Thus, the full answer is after the FD is determined to be unready. That will take at least two read()
s, and possibly more. If the file becomes ready after the last read and before the rearming then the rearming should cause an appropriate event to be queued.