Repository: netty/netty
Issue: NioEventLoop selector crash and can't recover

### Expected behavior


```
WARN [HSF-Worker-2-thread-5:t.h.remoting] [] [] [] Unexpected exception in the selector loop.
java.io.IOException: Broken pipe
	at sun.nio.ch.EPollArrayWrapper.interrupt(Native Method)
	at sun.nio.ch.EPollArrayWrapper.interrupt(EPollArrayWrapper.java:323)
	at sun.nio.ch.EPollSelectorImpl.wakeup(EPollSelectorImpl.java:207)
	at io.netty.channel.nio.SelectedSelectionKeySetSelector.wakeup(SelectedSelectionKeySetSelector.java:73)
	at io.netty.channel.nio.NioEventLoop.selectNow(NioEventLoop.java:722)
	at io.netty.channel.nio.NioEventLoop$1.get(NioEventLoop.java:71)
	at io.netty.channel.DefaultSelectStrategy.calculateStrategy(DefaultSelectStrategy.java:30)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:405)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
	at com.taobao.hsf.io.netty.util.PooledThreadFactory$PooledByteBufRunnable.run(PooledThreadFactory.java:37)
	at java.lang.Thread.run(Thread.java:882)

```

NioEventLoop can recover from exception or rebuild  a new selector

### Actual behavior

NioEventLoop dead totally

### Steps to reproduce

Can't reproduce this case,but we can mock;


see below code

```
protected void run() {
        for (;;) {
            try {
                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;
                    case SelectStrategy.SELECT:
                    .....
            } catch (Throwable t) {
                handleLoopException(t);
            }
```

notice that,`(selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())`  can throw an exception every loop;

now we look at `selectNowSupplier`,

```
    private final IntSupplier selectNowSupplier = new IntSupplier() {
        @Override
        public int get() throws Exception {
            return selectNow();
        }
    };
```

and I find `selector` api document

```
    /**
     * Selects a set of keys whose corresponding channels are ready for I/O
     * operations.
     *
     * <p> This method performs a non-blocking <a href="#selop">selection
     * operation</a>.  If no channels have become selectable since the previous
     * selection operation then this method immediately returns zero.
     *
     * <p> Invoking this method clears the effect of any previous invocations
     * of the {@link #wakeup wakeup} method.  </p>
     *
     * @return  The number of keys, possibly zero, whose ready-operation sets
     *          were updated by the selection operation
     *
     * @throws  IOException
     *          If an I/O error occurs
     *
     * @throws  ClosedSelectorException
     *          If this selector is closed
     */
    public abstract int selectNow() throws IOException;
```

java.nio.channels.Selector#selectNow may throw an exception ! but we don't handle it!


### Minimal yet complete reproducer code (or URL to code)

### Netty version
4.1.16.Final
### JVM version (e.g. `java -version`)
1.8
### OS version (e.g. `uname -a`)


Issue comments:
@qinliujie Please check https://github.com/netty/netty/pull/8569
@normanmaurer ok, thx~

Pull request review comments:
fix indenting?

Patch that resolved the issue:
diff --git a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java
index 187e1ec1a0b..f1aed11bc46 100644
--- a/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java
+++ b/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java
@@ -401,7 +401,8 @@ private void rebuildSelector0() {
     protected void run() {
         for (;;) {
             try {
-                switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
+                try {
+                    switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                     case SelectStrategy.CONTINUE:
                         continue;
 
@@ -444,6 +445,13 @@ protected void run() {
                         }
                         // fall through
                     default:
+                    }
+                } catch (IOException e) {
+                    // If we receive an IOException here its because the Selector is messed up. Let's rebuild
+                    // the selector and retry. https://github.com/netty/netty/issues/8566
+                    rebuildSelector0();
+                    handleLoopException(e);
+                    continue;
                 }
 
                 cancelledKeys = 0;


Test patch:
diff --git a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java
index 15fcb212442..8b176bc71c0 100644
--- a/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java
+++ b/transport/src/test/java/io/netty/channel/nio/NioEventLoopTest.java
@@ -19,16 +19,22 @@
 import io.netty.channel.Channel;
 import io.netty.channel.EventLoop;
 import io.netty.channel.EventLoopGroup;
+import io.netty.channel.SelectStrategy;
+import io.netty.channel.SelectStrategyFactory;
 import io.netty.channel.socket.ServerSocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
+import io.netty.util.IntSupplier;
+import io.netty.util.concurrent.DefaultThreadFactory;
 import io.netty.util.concurrent.Future;
 import org.hamcrest.core.IsInstanceOf;
 import org.junit.Test;
 
+import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.nio.channels.SelectionKey;
 import java.nio.channels.Selector;
 import java.nio.channels.SocketChannel;
+import java.nio.channels.spi.SelectorProvider;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.TimeUnit;
@@ -211,4 +217,45 @@ public void run() {
         }
     }
 
+    @Test
+    public void testRebuildSelectorOnIOException() {
+        SelectStrategyFactory selectStrategyFactory = new SelectStrategyFactory() {
+            @Override
+            public SelectStrategy newSelectStrategy() {
+                return new SelectStrategy() {
+
+                    private boolean thrown;
+
+                    @Override
+                    public int calculateStrategy(IntSupplier selectSupplier, boolean hasTasks) throws Exception {
+                        if (!thrown) {
+                            thrown = true;
+                            throw new IOException();
+                        }
+                        return -1;
+                    }
+                };
+            }
+        };
+
+        EventLoopGroup group = new NioEventLoopGroup(1, new DefaultThreadFactory("ioPool"),
+                                                     SelectorProvider.provider(), selectStrategyFactory);
+        final NioEventLoop loop = (NioEventLoop) group.next();
+        try {
+            Channel channel = new NioServerSocketChannel();
+            Selector selector = loop.unwrappedSelector();
+
+            loop.register(channel).syncUninterruptibly();
+
+            Selector newSelector = ((NioEventLoop) channel.eventLoop()).unwrappedSelector();
+            assertTrue(newSelector.isOpen());
+            assertNotSame(selector, newSelector);
+            assertFalse(selector.isOpen());
+
+            channel.close().syncUninterruptibly();
+        } finally {
+            group.shutdownGracefully();
+        }
+    }
+
 }