Does epoll guarantee that the first (or ongoing) call to epoll_wait after a file is registered with epoll_ctl for EPOLLIN and EPOLLET returns immediately in the case that the file was already readable prior to the epoll_ctl call? From my experiments with test programs, it appears that the answer is yes. Here are a couple examples to clarify my question:
Suppose we have initialized an epoll file efd
and a file fd
and the following event definition:
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET;
Now consider this scenario:
fd
epoll_ctl (efd, EPOLL_CTL_ADD, fd, &event);
epoll_wait (efd, events, MAXEVENTS, -1);
Now does the call in step 3 return immediately? In my experience it does. Is this guaranteed?
Now consider a second scenario, extending the first:
fd
epoll_ctl (efd, EPOLL_CTL_ADD, fd, &event);
epoll_wait (efd, events, MAXEVENTS, -1);
epoll_ctl (efd, EPOLL_CTL_MOD, fd, &event);
epoll_wait (efd, events, MAXEVENTS, -1);
Does the call in step 5 return immediately? In my experience it does. Is it guaranteed?
The epoll man pages are not entirely clear on this issue. In particular, the man pages suggest that you should always read from a file until EAGAIN is returned when using edge-triggered mode. But it seems like those comments are assuming that you are not re-registering the file whenever you want to wait on the file.
what is the purpose of epoll's edge triggered option? is a related discussion. The first two comments to the first answer seem to confirm that the behavior that I see is expected.
https://gist.github.com/3900742 is a C test program that illustrates that epoll with a pipe seems to behave as I've described it.
As epoll
is Linux specific there is no real specification, so it's pretty much up to what is actually implemented (the man pages try to describe that in some more user-friendly way, but don't provide all the details for edge-cases).
Looking at ep_insert and ep_modify both check the current event bits (irrespective of EPOLLET
):
/*
* Get current event bits. We can safely use the file* here because
* its usage count has been increased by the caller of this function.
*/
revents = epi->ffd.file->f_op->poll(epi->ffd.file, &pt);
So that explains the behaviour you are seeing and it seems to have been done deliberately. But as there is no specification, there is no cast-iron guarantee that the behaviour won't change in the future.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With