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) {
|
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) {
|
for (;;) {
|
||||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "eventfd_write() failed: ", errno);
|
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.epollFd = epollFd = Native.newEpollCreate();
|
||||||
this.eventFd = eventFd = Native.newEventFd();
|
this.eventFd = eventFd = Native.newEventFd();
|
||||||
try {
|
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) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to add eventFd filedescriptor to epoll", e);
|
throw new IllegalStateException("Unable to add eventFd filedescriptor to epoll", e);
|
||||||
}
|
}
|
||||||
this.timerFd = timerFd = Native.newTimerFd();
|
this.timerFd = timerFd = Native.newTimerFd();
|
||||||
try {
|
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);
|
Native.epollCtlAdd(epollFd.intValue(), timerFd.intValue(), Native.EPOLLIN | Native.EPOLLET);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException("Unable to add timerFd filedescriptor to epoll", 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) {
|
private void processReady(EpollEventArray events, int ready) {
|
||||||
for (int i = 0; i < ready; i ++) {
|
for (int i = 0; i < ready; i ++) {
|
||||||
final int fd = events.fd(i);
|
final int fd = events.fd(i);
|
||||||
if (fd == eventFd.intValue()) {
|
if (fd == eventFd.intValue() || fd == timerFd.intValue()) {
|
||||||
// consume wakeup event.
|
// Just ignore as we use ET mode for the eventfd and timerfd.
|
||||||
Native.eventFdRead(fd);
|
//
|
||||||
} else if (fd == timerFd.intValue()) {
|
// See also https://stackoverflow.com/a/12492308/1074097
|
||||||
// consume wakeup event, necessary because the timer is added with ET mode.
|
|
||||||
Native.timerFdRead(fd);
|
|
||||||
} else {
|
} else {
|
||||||
final long ev = events.events(i);
|
final long ev = events.events(i);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user