Don't read from timerfd and eventfd on each EventLoop tick (#9192)
Motivation: We do not need to issue a read on timerfd and eventfd when the EventLoop wakes up if we register these as Edge-Triggered. This removes the overhead of 2 syscalls and so helps to reduce latency. Modifications: - Ensure we register the timerfd and eventfd with EPOLLET flag - If eventfd_write fails with EAGAIN, call eventfd_read and try eventfd_write again as we only use it as wake-up mechanism. Result: Less syscalls and so reducing overhead. Co-authored-by: Carl Mastrangelo <carl@carlmastrangelo.com>
This commit is contained in:
parent
ede7251ecb
commit
f6cf681f90
@ -116,10 +116,27 @@ static jint netty_epoll_native_timerFd(JNIEnv* env, jclass clazz) {
|
||||
}
|
||||
|
||||
static void netty_epoll_native_eventFdWrite(JNIEnv* env, jclass clazz, jint fd, jlong value) {
|
||||
jint eventFD = eventfd_write(fd, (eventfd_t) value);
|
||||
uint64_t val;
|
||||
|
||||
if (eventFD < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_write() failed: ", errno);
|
||||
for (;;) {
|
||||
jint ret = eventfd_write(fd, (eventfd_t) value);
|
||||
|
||||
if (ret < 0) {
|
||||
// We need to read before we can write again, let's try to read and then write again and if this
|
||||
// fails we will bail out.
|
||||
//
|
||||
// See http://man7.org/linux/man-pages/man2/eventfd.2.html.
|
||||
if (errno == EAGAIN) {
|
||||
if (eventfd_read(fd, &val) == 0 || errno == EAGAIN) {
|
||||
// Try again
|
||||
continue;
|
||||
}
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_read(...) failed: ", errno);
|
||||
} else {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_write(...) failed: ", errno);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,12 +98,16 @@ class EpollEventLoop extends SingleThreadEventLoop {
|
||||
this.epollFd = epollFd = Native.newEpollCreate();
|
||||
this.eventFd = eventFd = Native.newEventFd();
|
||||
try {
|
||||
Native.epollCtlAdd(epollFd.intValue(), eventFd.intValue(), Native.EPOLLIN);
|
||||
// It is important to use EPOLLET here as we only want to get the notification once per
|
||||
// wakeup and don't call eventfd_read(...).
|
||||
Native.epollCtlAdd(epollFd.intValue(), eventFd.intValue(), Native.EPOLLIN | Native.EPOLLET);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unable to add eventFd filedescriptor to epoll", e);
|
||||
}
|
||||
this.timerFd = timerFd = Native.newTimerFd();
|
||||
try {
|
||||
// It is important to use EPOLLET here as we only want to get the notification once per
|
||||
// wakeup and don't call read(...).
|
||||
Native.epollCtlAdd(epollFd.intValue(), timerFd.intValue(), Native.EPOLLIN | Native.EPOLLET);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unable to add timerFd filedescriptor to epoll", e);
|
||||
@ -402,12 +406,10 @@ class EpollEventLoop extends SingleThreadEventLoop {
|
||||
private void processReady(EpollEventArray events, int ready) {
|
||||
for (int i = 0; i < ready; i ++) {
|
||||
final int fd = events.fd(i);
|
||||
if (fd == eventFd.intValue()) {
|
||||
// consume wakeup event.
|
||||
Native.eventFdRead(fd);
|
||||
} else if (fd == timerFd.intValue()) {
|
||||
// consume wakeup event, necessary because the timer is added with ET mode.
|
||||
Native.timerFdRead(fd);
|
||||
if (fd == eventFd.intValue() || fd == timerFd.intValue()) {
|
||||
// Just ignore as we use ET mode for the eventfd and timerfd.
|
||||
//
|
||||
// See also https://stackoverflow.com/a/12492308/1074097
|
||||
} else {
|
||||
final long ev = events.events(i);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user