Repository: netty/netty
Issue: About the bug of spliceTo()

When I use splice for traffic transmissions, if I still go to epollInReady when EPOLLRDHUP occurs, this will cause me to not receive channelInactive() events


### Expected behavior
when the other party closes the connection, I can receive channelinactive()
### Actual behavior
Cannot receive channelinactive(),and CPU 100%

![image](https://user-images.githubusercontent.com/41415014/155658646-0fff6505-f1b9-4066-9852-087f0040fd50.png)

### code

```java
            channel1.config().setAutoRead(false);
            channel2.config().setAutoRead(false);
            EpollSocketChannel ch1 = (EpollSocketChannel) channel1;
            EpollSocketChannel ch2 = (EpollSocketChannel) channel2;
            channel1.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                @Override
                public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                    ctx.channel().close();
                }
            });
            channel2.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                @Override
                public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                    ctx.channel().close();
                }
            });

            ch1.spliceTo(ch2, Integer.MAX_VALUE).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    if (!future.isSuccess()) {
                        future.cancel(true);
                        future.channel().close();
                    }
                }
            });
            ch2.spliceTo(ch1, Integer.MAX_VALUE).addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) {
                    if (!future.isSuccess()) {
                        future.cancel(true);
                        future.channel().close();
                    }
                }
            });
            channel1.config().setAutoRead(true);
            channel2.config().setAutoRead(true);

```

### Netty version
4.1.70.Final
### Jdk version
openjdk version "1.8.0_322"

### Linux version
linux 3.10.0-229.el7.x86_64

### Solution
Modify epollRdHupReady()

```java
       /**
         * Called once EPOLLRDHUP event is ready to be processed
         */
        final void epollRdHupReady() {
            // This must happen before we attempt to read. This will ensure reading continues until an error occurs.
            recvBufAllocHandle().receivedRdHup();

            if (isActive()) {
                // here
                if (AbstractEpollChannel.this instanceof AbstractEpollStreamChannel) {
                    if (((AbstractEpollStreamChannel) AbstractEpollChannel.this).spliceQueue != null) {
                        shutdownInput(true);
                    }
                } else {
                    // If it is still active, we need to call epollInReady as otherwise we may miss to
                    // read pending data from the underlying file descriptor.
                    // See https://github.com/netty/netty/issues/3709
                    epollInReady();
                }
            } else {
                // Just to be safe make sure the input marked as closed.
                shutdownInput(true);
            }

            // Clear the EPOLLRDHUP flag to prevent continuously getting woken up on this event.
            clearEpollRdHup();
        }
```


Issue comments:
Let me check... 
I have another question about spliceTo(), when native splice returns EAGAIN, why not suspend spliceTask and resume it when EPOLLIN is triggered next time.  Infinite calls to splice will fully load the CPU.
@AriseFX I am happy to review PRs. Please open a PR with fixes if you have time :)
> @AriseFX I am happy to review PRs. Please open a PR with fixes if you have time :)

I'll try :)
> @AriseFX I am happy to review PRs. Please open a PR with fixes if you have time :)

https://github.com/netty/netty/pull/12138

Pull request review comments:

Patch that resolved the issue:
diff --git a/transport-classes-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java b/transport-classes-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java
index 011b992b45f..0b7044bb8f3 100644
--- a/transport-classes-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java
+++ b/transport-classes-epoll/src/main/java/io/netty/channel/epoll/AbstractEpollStreamChannel.java
@@ -756,13 +756,18 @@ void epollInReady() {
 
             ByteBuf byteBuf = null;
             boolean close = false;
+            Queue<SpliceInTask> sQueue = null;
             try {
-                Queue<SpliceInTask> sQueue = null;
                 do {
                     if (sQueue != null || (sQueue = spliceQueue) != null) {
                         SpliceInTask spliceTask = sQueue.peek();
                         if (spliceTask != null) {
-                            if (spliceTask.spliceIn(allocHandle)) {
+                            boolean spliceInResult = spliceTask.spliceIn(allocHandle);
+
+                            if (allocHandle.isReceivedRdHup()) {
+                                shutdownInput(true);
+                            }
+                            if (spliceInResult) {
                                 // We need to check if it is still active as if not we removed all SpliceTasks in
                                 // doClose(...)
                                 if (isActive()) {
@@ -820,7 +825,13 @@ void epollInReady() {
             } catch (Throwable t) {
                 handleReadException(pipeline, byteBuf, t, close, allocHandle);
             } finally {
-                epollInFinally(config);
+                if (sQueue == null) {
+                    epollInFinally(config);
+                } else {
+                    if (!config.isAutoRead()) {
+                        clearEpollIn();
+                    }
+                }
             }
         }
     }
@@ -856,6 +867,7 @@ protected final int spliceIn(FileDescriptor pipeOut, RecvByteBufAllocator.Handle
             for (;;) {
                 // Splicing until there is nothing left to splice.
                 int localSplicedIn = Native.splice(socket.intValue(), -1, pipeOut.intValue(), -1, length);
+                handle.lastBytesRead(localSplicedIn);
                 if (localSplicedIn == 0) {
                     break;
                 }


Test patch: