The epoll manpage says that a fd registered with EPOLLET(edge triggered) shouldn't notify twice EPOLLIN if no read has been done.
So after an EPOLLIN you need to empty the buffer before epoll_wait being able to return a new EPOLLIN on new data.
However I'm experiencing problems with this approach as I'm seeing duplicated EPOLLIN events for untouched fds.
This is the strace output, 0x200 is EPOLLRDHUP that is not defined yet in my glibc headers but defined in the kernel.
30285 epoll_ctl(3, EPOLL_CTL_ADD, 9, {EPOLLIN|EPOLLPRI|EPOLLERR|EPOLLHUP|EPOLLET|0x2000, {u32=9, u64=9}}) = 0
30285 epoll_wait(3, {{EPOLLIN, {u32=9, u64=9}}}, 10, -1) = 1
30285 epoll_wait(3, {{EPOLLIN, {u32=9, u64=9}}}, 10, -1) = 1
30285 epoll_wait(3, <unfinished ...>
30349 epoll_ctl(3, EPOLL_CTL_DEL, 9, NULL) = 0
30306 recv(9, "7u\0\0\10\345\241\312\t\20\f\32\r\10\27\20\2\30\200\10 \31(C0\17\32\r\10\27\20\2\30"..., 20000, 0) = 20000
30349 epoll_ctl(3, EPOLL_CTL_DEL, 9, NULL) = -1 ENOENT (No such file or directory)
30305 recv(9, " \31(C0\17\32\r\10\27\20\2\30\200\10 \31(C0\17\32\r\10\27\20\2\30\200\10 \31("..., 20000, 0) = 10011
So, after adding fd number 9 I do receive 2 consecutive EPOLLIN events before having recving the file descriptor, the syscall trace shows how I do delete the fd before reading but it should only happen once, one per event.
So either I am not reading the manpage properly or something is now working here.
I think you missed this part of the epoll
man page:
Since even with the edge-triggered epoll multiple events can be generated upon receipt of multiple chunks of data, the caller has the option to specify the
EPOLLONESHOT
flag, to tell epoll to disable the associated file descriptor after the receipt of an event withepoll_wait(2)
. When theEPOLLONESHOT
flag is specified, it is the caller's responsibility to rearm the file descriptor usingepoll_ctl(2)
withEPOLL_CTL_MOD
.
That is: you got two chunks of data arriving in your receive queue before your first read()
happened, which means you got two epoll events. It seems like EPOLLONESHOT
is what you're after, which will atomically remove the file descriptor from the poll set when an event happens on it (so you won't need to do the EPOLL_CTL_DEL
).