Reputation: 1788
The man page on epoll_ctl(2)
has this to say about the EPOLLONESHOT
flag:
Sets the one-shot behavior for the associated file descriptor.
This means that after an event is pulled out with
epoll_wait(2) the associated file descriptor is internally
disabled and no other events will be reported by the epoll
interface. The user must call epoll_ctl() with EPOLL_CTL_MOD
to rearm the file descriptor with a new event mask.
However, it isn't clear whether the event is disabled in an epoll_wait
after being inserted into the event array or after all events have been returned.
Upvotes: 4
Views: 6650
Reputation: 21213
The behavior of EPOLLONESHOT
is such that after a successful call to epoll_wait(2)
where the specified file descriptor was reported, no new events will be reported by epoll_wait(2)
on the same file descriptor until you explicitly reactivate it with epoll_ctl(2)
. You can look at it as a mechanism of temporarily disabling a file descriptor once it is returned by epoll_wait(2)
.
It does not prevent epoll_wait(2)
from returning more than one event in the same call for the same file descriptor - in fact, if multiple events are available at the time of the call, they are all combined into the events
field of struct epoll_event
, whether or not EPOLLONESHOT
is in effect for that file descriptor.
In other words, EPOLLONESHOT
controls under what conditions a file descriptor is reported in a call to epoll_wait(2)
; it does not play a role in event aggregation and detection.
An Example
The code below creates a pair of connected sockets and writes to one end. sv[1]
will be available for read and write. As you can see, adding or removing EPOLLONESHOT
has no effect on the fact that EPOLLIN
and EPOLLOUT
are combined into one event.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(void) {
int efd = epoll_create(1);
if (efd < 0) {
perror("epoll_create(2) error");
exit(EXIT_FAILURE);
}
int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
perror("socketpair(2) error");
exit(EXIT_FAILURE);
}
const char str[] = "Hello, world!";
size_t to_write = sizeof(str)-1;
ssize_t written = write(sv[0], str, to_write);
if (written != to_write) {
if (written < 0) {
perror("write(2) error");
} else {
fprintf(stderr, "short write detected: %zd/%zu\n", written, to_write);
}
exit(EXIT_FAILURE);
}
struct epoll_event epevent;
epevent.events = EPOLLIN | EPOLLOUT | EPOLLONESHOT; // Try to remove EPOLLONESHOT here
epevent.data.fd = sv[1];
if (epoll_ctl(efd, EPOLL_CTL_ADD, sv[1], &epevent) < 0) {
perror("epoll_ctl(2) error");
exit(EXIT_FAILURE);
}
struct epoll_event events_arr[16];
int events = epoll_wait(efd, events_arr, sizeof(events_arr)/sizeof(*events_arr), -1);
if (events < 0) {
perror("epoll_wait(2) error");
exit(EXIT_FAILURE);
}
int i;
for (i = 0; i < events; i++) {
printf("Event %d: ", i);
if (events_arr[i].events & EPOLLIN)
printf("EPOLLIN ");
if (events_arr[i].events & EPOLLOUT)
printf("EPOLLOUT ");
printf("\n");
}
return 0;
}
Upvotes: 10