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.Collection;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
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!
*/
final class EpollEventLoop extends SingleThreadEventLoop {
class EpollEventLoop extends SingleThreadEventLoop {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(EpollEventLoop.class);
private static final AtomicIntegerFieldUpdater<EpollEventLoop> WAKEN_UP_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(EpollEventLoop.class, "wakenUp");
@ -75,6 +74,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
return epollWaitNow();
}
};
@SuppressWarnings("unused") // AtomicIntegerFieldUpdater
private volatile int wakenUp;
private volatile int ioRatio = 50;
@ -248,7 +248,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
long totalDelay = delayNanos(System.nanoTime());
prevDeadlineNanos = curDeadlineNanos;
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);
}
@ -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);
// Prevent possible consecutive immediate failures that lead to

View File

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