diff --git a/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java b/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java index 5ecd18668d..f4708bda00 100644 --- a/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/channel/epoll/EpollSocketChannelBenchmark.java @@ -30,10 +30,15 @@ import io.netty.microbench.util.AbstractMicrobenchmark; import io.netty.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.GroupThreads; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.TearDown; public class EpollSocketChannelBenchmark extends AbstractMicrobenchmark { + private static final Runnable runnable = new Runnable() { + @Override + public void run() { } + }; private EpollEventLoopGroup group; private Channel serverChan; @@ -136,4 +141,15 @@ public class EpollSocketChannelBenchmark extends AbstractMicrobenchmark { public Object pingPong() throws Exception { return chan.pipeline().writeAndFlush(abyte.retainedSlice()).sync(); } + + @Benchmark + public Object executeSingle() throws Exception { + return chan.eventLoop().submit(runnable).get(); + } + + @Benchmark + @GroupThreads(3) + public Object executeMulti() throws Exception { + return chan.eventLoop().submit(runnable).get(); + } } 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 8c9ad17821..d99f4a5d48 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 @@ -166,7 +166,7 @@ class EpollEventLoop extends SingleThreadEventLoop { @Override protected void wakeup(boolean inEventLoop) { - if (!inEventLoop && WAKEN_UP_UPDATER.compareAndSet(this, 0, 1)) { + if (!inEventLoop && WAKEN_UP_UPDATER.getAndSet(this, 1) == 0) { // write to the evfd which will then wake-up epoll_wait(...) Native.eventFdWrite(eventFd.intValue(), 1L); } @@ -245,15 +245,7 @@ class EpollEventLoop extends SingleThreadEventLoop { return channels.size(); } - private int epollWait(boolean oldWakeup) throws IOException { - // If a task was submitted when wakenUp value was 1, the task didn't get a chance to produce wakeup event. - // So we need to check task queue again before calling epoll_wait. If we don't, the task might be pended - // until epoll_wait was timed out. It might be pended until idle timeout if IdleStateHandler existed - // in pipeline. - if (oldWakeup && hasTasks()) { - return epollWaitNow(); - } - + private int epollWait() throws IOException { int delaySeconds; int delayNanos; long curDeadlineNanos = deadlineNanos(); @@ -291,38 +283,11 @@ class EpollEventLoop extends SingleThreadEventLoop { break; case SelectStrategy.SELECT: - strategy = epollWait(WAKEN_UP_UPDATER.getAndSet(this, 0) == 1); - - // 'wakenUp.compareAndSet(false, true)' is always evaluated - // before calling 'selector.wakeup()' to reduce the wake-up - // overhead. (Selector.wakeup() is an expensive operation.) - // - // However, there is a race condition in this approach. - // The race condition is triggered when 'wakenUp' is set to - // true too early. - // - // 'wakenUp' is set to true too early if: - // 1) Selector is waken up between 'wakenUp.set(false)' and - // 'selector.select(...)'. (BAD) - // 2) Selector is waken up between 'selector.select(...)' and - // 'if (wakenUp.get()) { ... }'. (OK) - // - // In the first case, 'wakenUp' is set to true and the - // following 'selector.select(...)' will wake up immediately. - // Until 'wakenUp' is set to false again in the next round, - // 'wakenUp.compareAndSet(false, true)' will fail, and therefore - // any attempt to wake up the Selector will fail, too, causing - // the following 'selector.select(...)' call to block - // unnecessarily. - // - // To fix this problem, we wake up the selector again if wakenUp - // is true immediately after selector.select(...). - // It is inefficient in that it wakes up the selector for both - // the first case (BAD - wake-up required) and the second case - // (OK - no wake-up required). - if (wakenUp == 1) { - Native.eventFdWrite(eventFd.intValue(), 1L); + wakenUp = 0; + } + if (!hasTasks()) { + strategy = epollWait(); } // fallthrough default: