epoll_wait produces an EINVAL error since 4.1.30 (#8350)

Motivation:

epoll_wait should work in 4.1.30 like it did in 4.1.29.

Modifications:

Revert Integer.MAX_VALUE back to MAX_SCHEDULED_TIMERFD_NS (999,999,999).
Add unit test.

Result:

epoll_wait will no longer throw EINVAL.
This commit is contained in:
Johno Crawford 2018-10-12 05:02:41 +02:00 committed by Norman Maurer
parent 2a4bb346cf
commit 5b3b8db07f
2 changed files with 36 additions and 13 deletions

View File

@ -35,7 +35,6 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
@ -44,7 +43,7 @@ import static java.lang.Math.min;
/** /**
* {@link EventLoop} which uses epoll under the covers. Only works on Linux! * {@link EventLoop} which uses epoll under the covers. Only works on Linux!
*/ */
final class EpollEventLoop extends SingleThreadEventLoop { class EpollEventLoop extends SingleThreadEventLoop {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(EpollEventLoop.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(EpollEventLoop.class);
private static final AtomicIntegerFieldUpdater<EpollEventLoop> WAKEN_UP_UPDATER = private static final AtomicIntegerFieldUpdater<EpollEventLoop> WAKEN_UP_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp"); AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp");
@ -75,6 +74,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
return epollWaitNow(); return epollWaitNow();
} }
}; };
@SuppressWarnings("unused") // AtomicIntegerFieldUpdater
private volatile int wakenUp; private volatile int wakenUp;
private volatile int ioRatio = 50; private volatile int ioRatio = 50;
@ -248,7 +248,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
long totalDelay = delayNanos(System.nanoTime()); long totalDelay = delayNanos(System.nanoTime());
prevDeadlineNanos = curDeadlineNanos; prevDeadlineNanos = curDeadlineNanos;
delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE); delaySeconds = (int) min(totalDelay / 1000000000L, Integer.MAX_VALUE);
delayNanos = (int) min(totalDelay - delaySeconds * 1000000000L, Integer.MAX_VALUE); delayNanos = (int) min(totalDelay - delaySeconds * 1000000000L, MAX_SCHEDULED_TIMERFD_NS);
} }
return Native.epollWait(epollFd, events, timerFd, delaySeconds, delayNanos); return Native.epollWait(epollFd, events, timerFd, delaySeconds, delayNanos);
} }
@ -356,7 +356,10 @@ final class EpollEventLoop extends SingleThreadEventLoop {
} }
} }
private static void handleLoopException(Throwable t) { /**
* Visible only for testing!
*/
void handleLoopException(Throwable t) {
logger.warn("Unexpected exception in the selector loop.", t); logger.warn("Unexpected exception in the selector loop.", t);
// Prevent possible consecutive immediate failures that lead to // Prevent possible consecutive immediate failures that lead to

View File

@ -15,32 +15,52 @@
*/ */
package io.netty.channel.epoll; package io.netty.channel.epoll;
import io.netty.channel.DefaultSelectStrategyFactory;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.RejectedExecutionHandlers;
import io.netty.util.concurrent.ThreadPerTaskExecutor;
import org.junit.Test; import org.junit.Test;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class EpollEventLoopTest { public class EpollEventLoopTest {
@Test @Test
public void testScheduleBigDelayNotOverflow() { public void testScheduleBigDelayNotOverflow() {
EventLoopGroup group = new EpollEventLoopGroup(1); final AtomicReference<Throwable> capture = new AtomicReference<Throwable>();
final EventLoop el = group.next(); final EventLoopGroup group = new EpollEventLoop(null,
Future<?> future = el.schedule(new Runnable() { new ThreadPerTaskExecutor(new DefaultThreadFactory(getClass())), 0,
DefaultSelectStrategyFactory.INSTANCE.newSelectStrategy(), RejectedExecutionHandlers.reject()) {
@Override @Override
public void run() { void handleLoopException(Throwable t) {
// NOOP capture.set(t);
super.handleLoopException(t);
} }
}, Long.MAX_VALUE, TimeUnit.MILLISECONDS); };
assertFalse(future.awaitUninterruptibly(1000)); try {
assertTrue(future.cancel(true)); final EventLoop eventLoop = group.next();
group.shutdownGracefully(); Future<?> future = eventLoop.schedule(new Runnable() {
@Override
public void run() {
// NOOP
}
}, Long.MAX_VALUE, TimeUnit.MILLISECONDS);
assertFalse(future.awaitUninterruptibly(1000));
assertTrue(future.cancel(true));
assertNull(capture.get());
} finally {
group.shutdownGracefully();
}
} }
} }