diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java index be5814f595..6795901615 100644 --- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java +++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java @@ -443,6 +443,19 @@ public abstract class SingleThreadEventExecutor extends AbstractScheduledEventEx return scheduledTask.delayNanos(currentTimeNanos); } + /** + * Returns the absolute point in time (relative to {@link #nanoTime()}) at which the the next + * closest scheduled task should run. + */ + @UnstableApi + protected long deadlineNanos() { + ScheduledFutureTask scheduledTask = peekScheduledTask(); + if (scheduledTask == null) { + return nanoTime() + SCHEDULE_PURGE_INTERVAL; + } + return scheduledTask.deadlineNanos(); + } + /** * Updates the internal timestamp that tells when a submitted task was executed most recently. * {@link #runAllTasks()} and {@link #runAllTasks(long)} updates this timestamp automatically, and thus there's diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index e6a38b4f23..19a8372419 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -182,13 +182,17 @@ static jint netty_epoll_native_epollWait0(JNIEnv* env, jclass clazz, jint efd, j } } while((err = errno) == EINTR); } else { - struct itimerspec ts; - memset(&ts.it_interval, 0, sizeof(struct timespec)); - ts.it_value.tv_sec = tvSec; - ts.it_value.tv_nsec = tvNsec; - if (timerfd_settime(timerFd, 0, &ts, NULL) < 0) { - netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_settime() failed: ", errno); - return -1; + // only reschedule the timer if there is a newer event. + // -1 is a special value used by EpollEventLoop. + if (tvSec != ((jint) -1) && tvNsec != ((jint) -1)) { + struct itimerspec ts; + memset(&ts.it_interval, 0, sizeof(struct timespec)); + ts.it_value.tv_sec = tvSec; + ts.it_value.tv_nsec = tvNsec; + if (timerfd_settime(timerFd, 0, &ts, NULL) < 0) { + netty_unix_errors_throwChannelExceptionErrorNo(env, "timerfd_settime() failed: ", errno); + return -1; + } } do { result = epoll_wait(efd, ev, len, -1); diff --git a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java index c90a2ca106..462b0b0a33 100644 --- a/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java +++ b/transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollEventLoop.java @@ -55,6 +55,8 @@ final class EpollEventLoop extends SingleThreadEventLoop { Epoll.ensureAvailability(); } + // Pick a number that no task could have previously used. + private long prevDeadlineNanos = nanoTime() - 1; private final FileDescriptor epollFd; private final FileDescriptor eventFd; private final FileDescriptor timerFd; @@ -236,10 +238,19 @@ final class EpollEventLoop extends SingleThreadEventLoop { return epollWaitNow(); } - long totalDelay = delayNanos(System.nanoTime()); - int delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); - return Native.epollWait(epollFd, events, timerFd, delaySeconds, - (int) min(MAX_SCHEDULED_TIMERFD_NS, totalDelay - delaySeconds * 1000000000L)); + int delaySeconds; + int delayNanos; + long curDeadlineNanos = deadlineNanos(); + if (curDeadlineNanos == prevDeadlineNanos) { + delaySeconds = -1; + delayNanos = -1; + } else { + long totalDelay = delayNanos(System.nanoTime()); + prevDeadlineNanos = curDeadlineNanos; + delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); + delayNanos = (int) min(totalDelay - delaySeconds * 1000000000L, Integer.MAX_VALUE); + } + return Native.epollWait(epollFd, events, timerFd, delaySeconds, delayNanos); } private int epollWaitNow() throws IOException {