View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.epoll;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.buffer.ByteBufAllocator;
20  import io.netty.channel.Channel;
21  import io.netty.channel.ChannelConfig;
22  import io.netty.channel.ChannelFuture;
23  import io.netty.channel.ChannelFutureListener;
24  import io.netty.channel.ChannelMetadata;
25  import io.netty.channel.ChannelOutboundBuffer;
26  import io.netty.channel.ChannelPipeline;
27  import io.netty.channel.ChannelPromise;
28  import io.netty.channel.DefaultFileRegion;
29  import io.netty.channel.EventLoop;
30  import io.netty.channel.FileRegion;
31  import io.netty.channel.RecvByteBufAllocator;
32  import io.netty.channel.internal.ChannelUtils;
33  import io.netty.channel.socket.DuplexChannel;
34  import io.netty.channel.unix.FileDescriptor;
35  import io.netty.channel.unix.IovArray;
36  import io.netty.channel.unix.SocketWritableByteChannel;
37  import io.netty.channel.unix.UnixChannelUtil;
38  import io.netty.util.LeakPresenceDetector;
39  import io.netty.util.internal.PlatformDependent;
40  import io.netty.util.internal.StringUtil;
41  import io.netty.util.internal.logging.InternalLogger;
42  import io.netty.util.internal.logging.InternalLoggerFactory;
43  
44  import java.io.IOException;
45  import java.net.SocketAddress;
46  import java.nio.ByteBuffer;
47  import java.nio.channels.ClosedChannelException;
48  import java.nio.channels.WritableByteChannel;
49  import java.util.Queue;
50  import java.util.concurrent.Executor;
51  
52  import static io.netty.channel.internal.ChannelUtils.MAX_BYTES_PER_GATHERING_WRITE_ATTEMPTED_LOW_THRESHOLD;
53  import static io.netty.channel.internal.ChannelUtils.WRITE_STATUS_SNDBUF_FULL;
54  import static io.netty.channel.unix.FileDescriptor.pipe;
55  import static io.netty.util.internal.ObjectUtil.checkNotNull;
56  import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
57  import static io.netty.util.internal.StringUtil.className;
58  
59  public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel implements DuplexChannel {
60      private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
61      private static final String EXPECTED_TYPES =
62              " (expected: " + StringUtil.simpleClassName(ByteBuf.class) + ", " +
63                      StringUtil.simpleClassName(DefaultFileRegion.class) + ')';
64      private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractEpollStreamChannel.class);
65  
66      private final Runnable flushTask = new Runnable() {
67          @Override
68          public void run() {
69              // Calling flush0 directly to ensure we not try to flush messages that were added via write(...) in the
70              // meantime.
71              ((AbstractEpollUnsafe) unsafe()).flush0();
72          }
73      };
74  
75      // Lazy init these if we need to splice(...)
76      private volatile Queue<SpliceInTask> spliceQueue;
77      private FileDescriptor pipeIn;
78      private FileDescriptor pipeOut;
79  
80      private WritableByteChannel byteChannel;
81  
82      protected AbstractEpollStreamChannel(Channel parent, int fd) {
83          this(parent, new LinuxSocket(fd));
84      }
85  
86      protected AbstractEpollStreamChannel(int fd) {
87          this(new LinuxSocket(fd));
88      }
89  
90      AbstractEpollStreamChannel(LinuxSocket fd) {
91          this(fd, isSoErrorZero(fd));
92      }
93  
94      AbstractEpollStreamChannel(Channel parent, LinuxSocket fd) {
95          // Add EPOLLRDHUP so we are notified once the remote peer close the connection.
96          super(parent, fd, true, EpollIoOps.EPOLLRDHUP);
97      }
98  
99      protected AbstractEpollStreamChannel(Channel parent, LinuxSocket fd, SocketAddress remote) {
100         // Add EPOLLRDHUP so we are notified once the remote peer close the connection.
101         super(parent, fd, remote, EpollIoOps.EPOLLRDHUP);
102     }
103 
104     protected AbstractEpollStreamChannel(LinuxSocket fd, boolean active) {
105         // Add EPOLLRDHUP so we are notified once the remote peer close the connection.
106         super(null, fd, active, EpollIoOps.EPOLLRDHUP);
107     }
108 
109     @Override
110     protected AbstractEpollUnsafe newUnsafe() {
111         return new EpollStreamUnsafe();
112     }
113 
114     @Override
115     public ChannelMetadata metadata() {
116         return METADATA;
117     }
118 
119     /**
120      * Splice from this {@link AbstractEpollStreamChannel} to another {@link AbstractEpollStreamChannel}.
121      * The {@code len} is the number of bytes to splice. If using {@link Integer#MAX_VALUE} it will
122      * splice until the {@link ChannelFuture} was canceled or it was failed.
123      *
124      * Please note:
125      * <ul>
126      *   <li>both channels need to be registered to the same {@link EventLoop}, otherwise an
127      *   {@link IllegalArgumentException} is thrown. </li>
128      *   <li>{@link EpollChannelConfig#getEpollMode()} must be {@link EpollMode#LEVEL_TRIGGERED} for this and the
129      *   target {@link AbstractEpollStreamChannel}</li>
130      * </ul>
131      * @deprecated Will be removed in the future.
132      */
133     @Deprecated
134     public final ChannelFuture spliceTo(final AbstractEpollStreamChannel ch, final int len) {
135         return spliceTo(ch, len, newPromise());
136     }
137 
138     /**
139      * Splice from this {@link AbstractEpollStreamChannel} to another {@link AbstractEpollStreamChannel}.
140      * The {@code len} is the number of bytes to splice. If using {@link Integer#MAX_VALUE} it will
141      * splice until the {@link ChannelFuture} was canceled or it was failed.
142      *
143      * Please note:
144      * <ul>
145      *   <li>both channels need to be registered to the same {@link EventLoop}, otherwise an
146      *   {@link IllegalArgumentException} is thrown. </li>
147      *   <li>{@link EpollChannelConfig#getEpollMode()} must be {@link EpollMode#LEVEL_TRIGGERED} for this and the
148      *   target {@link AbstractEpollStreamChannel}</li>
149      * </ul>
150      * @deprecated will be removed in the future.
151      */
152     @Deprecated
153     public final ChannelFuture spliceTo(final AbstractEpollStreamChannel ch, final int len,
154                                         final ChannelPromise promise) {
155         if (ch.eventLoop() != eventLoop()) {
156             throw new IllegalArgumentException("EventLoops are not the same.");
157         }
158         checkPositiveOrZero(len, "len");
159         if (ch.config().getEpollMode() != EpollMode.LEVEL_TRIGGERED
160                 || config().getEpollMode() != EpollMode.LEVEL_TRIGGERED) {
161             throw new IllegalStateException("spliceTo() supported only when using " + EpollMode.LEVEL_TRIGGERED);
162         }
163         checkNotNull(promise, "promise");
164         if (!isOpen()) {
165             promise.tryFailure(new ClosedChannelException());
166         } else {
167             addToSpliceQueue(new SpliceInChannelTask(ch, len, promise));
168             failSpliceIfClosed(promise);
169         }
170         return promise;
171     }
172 
173     /**
174      * Splice from this {@link AbstractEpollStreamChannel} to another {@link FileDescriptor}.
175      * The {@code offset} is the offset for the {@link FileDescriptor} and {@code len} is the
176      * number of bytes to splice. If using {@link Integer#MAX_VALUE} it will splice until the
177      * {@link ChannelFuture} was canceled or it was failed.
178      *
179      * Please note:
180      * <ul>
181      *   <li>{@link EpollChannelConfig#getEpollMode()} must be {@link EpollMode#LEVEL_TRIGGERED} for this
182      *   {@link AbstractEpollStreamChannel}</li>
183      *   <li>the {@link FileDescriptor} will not be closed after the {@link ChannelFuture} is notified</li>
184      *   <li>this channel must be registered to an event loop or {@link IllegalStateException} will be thrown.</li>
185      * </ul>
186      * @deprecated Will be removed in the future.
187      */
188     @Deprecated
189     public final ChannelFuture spliceTo(final FileDescriptor ch, final int offset, final int len) {
190         return spliceTo(ch, offset, len, newPromise());
191     }
192 
193     /**
194      * Splice from this {@link AbstractEpollStreamChannel} to another {@link FileDescriptor}.
195      * The {@code offset} is the offset for the {@link FileDescriptor} and {@code len} is the
196      * number of bytes to splice. If using {@link Integer#MAX_VALUE} it will splice until the
197      * {@link ChannelFuture} was canceled or it was failed.
198      *
199      * Please note:
200      * <ul>
201      *   <li>{@link EpollChannelConfig#getEpollMode()} must be {@link EpollMode#LEVEL_TRIGGERED} for this
202      *   {@link AbstractEpollStreamChannel}</li>
203      *   <li>the {@link FileDescriptor} will not be closed after the {@link ChannelPromise} is notified</li>
204      *   <li>this channel must be registered to an event loop or {@link IllegalStateException} will be thrown.</li>
205      * </ul>
206      * @deprecated Will be removed in the future.
207      */
208     @Deprecated
209     public final ChannelFuture spliceTo(final FileDescriptor ch, final int offset, final int len,
210                                         final ChannelPromise promise) {
211         checkPositiveOrZero(len, "len");
212         checkPositiveOrZero(offset, "offset");
213         if (config().getEpollMode() != EpollMode.LEVEL_TRIGGERED) {
214             throw new IllegalStateException("spliceTo() supported only when using " + EpollMode.LEVEL_TRIGGERED);
215         }
216         checkNotNull(promise, "promise");
217         if (!isOpen()) {
218             promise.tryFailure(new ClosedChannelException());
219         } else {
220             addToSpliceQueue(new SpliceFdTask(ch, offset, len, promise));
221             failSpliceIfClosed(promise);
222         }
223         return promise;
224     }
225 
226     private void failSpliceIfClosed(ChannelPromise promise) {
227         if (!isOpen()) {
228             // Seems like the Channel was closed in the meantime try to fail the promise to prevent any
229             // cases where a future may not be notified otherwise.
230             if (!promise.isDone()) {
231                 final ClosedChannelException ex = new ClosedChannelException();
232                 if (promise.tryFailure(ex)) {
233                     eventLoop().execute(new Runnable() {
234                         @Override
235                         public void run() {
236                             // Call this via the EventLoop as it is a MPSC queue.
237                             clearSpliceQueue(ex);
238                         }
239                     });
240                 }
241             }
242         }
243     }
244 
245     /**
246      * Write bytes form the given {@link ByteBuf} to the underlying {@link java.nio.channels.Channel}.
247      * @param in the collection which contains objects to write.
248      * @param buf the {@link ByteBuf} from which the bytes should be written
249      * @return The value that should be decremented from the write quantum which starts at
250      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
251      * <ul>
252      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
253      *     is encountered</li>
254      *     <li>1 - if a single call to write data was made to the OS</li>
255      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
256      *     no data was accepted</li>
257      * </ul>
258      */
259     private int writeBytes(ChannelOutboundBuffer in, ByteBuf buf) throws Exception {
260         int readableBytes = buf.readableBytes();
261         if (readableBytes == 0) {
262             in.remove();
263             return 0;
264         }
265 
266         if (buf.hasMemoryAddress() || buf.nioBufferCount() == 1) {
267             return doWriteBytes(in, buf);
268         } else {
269             ByteBuffer[] nioBuffers = buf.nioBuffers();
270             return writeBytesMultiple(in, nioBuffers, nioBuffers.length, readableBytes,
271                     config().getMaxBytesPerGatheringWrite());
272         }
273     }
274 
275     private void adjustMaxBytesPerGatheringWrite(long attempted, long written, long oldMaxBytesPerGatheringWrite) {
276         // By default we track the SO_SNDBUF when ever it is explicitly set. However some OSes may dynamically change
277         // SO_SNDBUF (and other characteristics that determine how much data can be written at once) so we should try
278         // make a best effort to adjust as OS behavior changes.
279         if (attempted == written) {
280             if (attempted << 1 > oldMaxBytesPerGatheringWrite) {
281                 config().setMaxBytesPerGatheringWrite(attempted << 1);
282             }
283         } else if (attempted > MAX_BYTES_PER_GATHERING_WRITE_ATTEMPTED_LOW_THRESHOLD && written < attempted >>> 1) {
284             config().setMaxBytesPerGatheringWrite(attempted >>> 1);
285         }
286     }
287 
288     /**
289      * Write multiple bytes via {@link IovArray}.
290      * @param in the collection which contains objects to write.
291      * @param array The array which contains the content to write.
292      * @return The value that should be decremented from the write quantum which starts at
293      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
294      * <ul>
295      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
296      *     is encountered</li>
297      *     <li>1 - if a single call to write data was made to the OS</li>
298      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
299      *     no data was accepted</li>
300      * </ul>
301      * @throws IOException If an I/O exception occurs during write.
302      */
303     private int writeBytesMultiple(ChannelOutboundBuffer in, IovArray array) throws IOException {
304         final long expectedWrittenBytes = array.size();
305         assert expectedWrittenBytes != 0;
306         final int cnt = array.count();
307         assert cnt != 0;
308 
309         final long localWrittenBytes = socket.writevAddresses(array.memoryAddress(0), cnt);
310         if (localWrittenBytes > 0) {
311             adjustMaxBytesPerGatheringWrite(expectedWrittenBytes, localWrittenBytes, array.maxBytes());
312             in.removeBytes(localWrittenBytes);
313             return 1;
314         }
315         return WRITE_STATUS_SNDBUF_FULL;
316     }
317 
318     /**
319      * Write multiple bytes via {@link ByteBuffer} array.
320      * @param in the collection which contains objects to write.
321      * @param nioBuffers The buffers to write.
322      * @param nioBufferCnt The number of buffers to write.
323      * @param expectedWrittenBytes The number of bytes we expect to write.
324      * @param maxBytesPerGatheringWrite The maximum number of bytes we should attempt to write.
325      * @return The value that should be decremented from the write quantum which starts at
326      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
327      * <ul>
328      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
329      *     is encountered</li>
330      *     <li>1 - if a single call to write data was made to the OS</li>
331      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
332      *     no data was accepted</li>
333      * </ul>
334      * @throws IOException If an I/O exception occurs during write.
335      */
336     private int writeBytesMultiple(
337             ChannelOutboundBuffer in, ByteBuffer[] nioBuffers, int nioBufferCnt, long expectedWrittenBytes,
338             long maxBytesPerGatheringWrite) throws IOException {
339         assert expectedWrittenBytes != 0;
340         if (expectedWrittenBytes > maxBytesPerGatheringWrite) {
341             expectedWrittenBytes = maxBytesPerGatheringWrite;
342         }
343 
344         final long localWrittenBytes = socket.writev(nioBuffers, 0, nioBufferCnt, expectedWrittenBytes);
345         if (localWrittenBytes > 0) {
346             adjustMaxBytesPerGatheringWrite(expectedWrittenBytes, localWrittenBytes, maxBytesPerGatheringWrite);
347             in.removeBytes(localWrittenBytes);
348             return 1;
349         }
350         return WRITE_STATUS_SNDBUF_FULL;
351     }
352 
353     /**
354      * Write a {@link DefaultFileRegion}
355      * @param in the collection which contains objects to write.
356      * @param region the {@link DefaultFileRegion} from which the bytes should be written
357      * @return The value that should be decremented from the write quantum which starts at
358      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
359      * <ul>
360      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
361      *     is encountered</li>
362      *     <li>1 - if a single call to write data was made to the OS</li>
363      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
364      *     no data was accepted</li>
365      * </ul>
366      */
367     private int writeDefaultFileRegion(ChannelOutboundBuffer in, DefaultFileRegion region) throws Exception {
368         final long offset = region.transferred();
369         final long regionCount = region.count();
370         if (offset >= regionCount) {
371             in.remove();
372             return 0;
373         }
374 
375         final long flushedAmount = socket.sendFile(region, region.position(), offset, regionCount - offset);
376         if (flushedAmount > 0) {
377             in.progress(flushedAmount);
378             if (region.transferred() >= regionCount) {
379                 in.remove();
380             }
381             return 1;
382         } else if (flushedAmount == 0) {
383             validateFileRegion(region, offset);
384         }
385         return WRITE_STATUS_SNDBUF_FULL;
386     }
387 
388     /**
389      * Write a {@link FileRegion}
390      * @param in the collection which contains objects to write.
391      * @param region the {@link FileRegion} from which the bytes should be written
392      * @return The value that should be decremented from the write quantum which starts at
393      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
394      * <ul>
395      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
396      *     is encountered</li>
397      *     <li>1 - if a single call to write data was made to the OS</li>
398      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
399      *     no data was accepted</li>
400      * </ul>
401      */
402     private int writeFileRegion(ChannelOutboundBuffer in, FileRegion region) throws Exception {
403         if (region.transferred() >= region.count()) {
404             in.remove();
405             return 0;
406         }
407 
408         if (byteChannel == null) {
409             byteChannel = new EpollSocketWritableByteChannel();
410         }
411         final long flushedAmount = region.transferTo(byteChannel, region.transferred());
412         if (flushedAmount > 0) {
413             in.progress(flushedAmount);
414             if (region.transferred() >= region.count()) {
415                 in.remove();
416             }
417             return 1;
418         }
419         return WRITE_STATUS_SNDBUF_FULL;
420     }
421 
422     @Override
423     protected void doWrite(ChannelOutboundBuffer in) throws Exception {
424         // Don't write the initial data that was sent by TCP_FASTOPEN_CONNECT a second time.
425         removeFastOpenData();
426         int writeSpinCount = config().getWriteSpinCount();
427         do {
428             final int msgCount = in.size();
429             // Do gathering write if the outbound buffer entries start with more than one ByteBuf.
430             if (msgCount > 1 && in.current() instanceof ByteBuf) {
431                 writeSpinCount -= doWriteMultiple(in);
432             } else if (msgCount == 0) {
433                 // Wrote all messages.
434                 clearFlag(Native.EPOLLOUT);
435                 // Return here so we not set the EPOLLOUT flag.
436                 return;
437             } else {  // msgCount == 1
438                 writeSpinCount -= doWriteSingle(in);
439             }
440 
441             // We do not break the loop here even if the outbound buffer was flushed completely,
442             // because a user might have triggered another write and flush when we notify his or her
443             // listeners.
444         } while (writeSpinCount > 0);
445 
446         if (writeSpinCount == 0) {
447             // It is possible that we have set EPOLLOUT, woken up by EPOLL because the socket is writable, and then use
448             // our write quantum. In this case we no longer want to set the EPOLLOUT flag because the socket is still
449             // writable (as far as we know). We will find out next time we attempt to write if the socket is writable
450             // and set the EPOLLOUT if necessary.
451             clearFlag(Native.EPOLLOUT);
452 
453             // We used our writeSpin quantum, and should try to write again later.
454             eventLoop().execute(flushTask);
455         } else {
456             // Underlying descriptor can not accept all data currently, so set the EPOLLOUT flag to be woken up
457             // when it can accept more data.
458             setFlag(Native.EPOLLOUT);
459         }
460     }
461 
462     /**
463      * Attempt to write a single object.
464      * @param in the collection which contains objects to write.
465      * @return The value that should be decremented from the write quantum which starts at
466      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
467      * <ul>
468      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
469      *     is encountered</li>
470      *     <li>1 - if a single call to write data was made to the OS</li>
471      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
472      *     no data was accepted</li>
473      * </ul>
474      * @throws Exception If an I/O error occurs.
475      */
476     protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception {
477         // The outbound buffer contains only one message or it contains a file region.
478         Object msg = in.current();
479         if (msg instanceof ByteBuf) {
480             return writeBytes(in, (ByteBuf) msg);
481         } else if (msg instanceof DefaultFileRegion) {
482             return writeDefaultFileRegion(in, (DefaultFileRegion) msg);
483         } else if (msg instanceof FileRegion) {
484             return writeFileRegion(in, (FileRegion) msg);
485         } else if (msg instanceof SpliceOutTask) {
486             if (!((SpliceOutTask) msg).spliceOut()) {
487                 return WRITE_STATUS_SNDBUF_FULL;
488             }
489             in.remove();
490             return 1;
491         } else {
492             // Should never reach here.
493             throw new Error("Unexpected message type: " + className(msg));
494         }
495     }
496 
497     /**
498      * Attempt to write multiple {@link ByteBuf} objects.
499      * @param in the collection which contains objects to write.
500      * @return The value that should be decremented from the write quantum which starts at
501      * {@link ChannelConfig#getWriteSpinCount()}. The typical use cases are as follows:
502      * <ul>
503      *     <li>0 - if no write was attempted. This is appropriate if an empty {@link ByteBuf} (or other empty content)
504      *     is encountered</li>
505      *     <li>1 - if a single call to write data was made to the OS</li>
506      *     <li>{@link ChannelUtils#WRITE_STATUS_SNDBUF_FULL} - if an attempt to write data was made to the OS, but
507      *     no data was accepted</li>
508      * </ul>
509      * @throws Exception If an I/O error occurs.
510      */
511     private int doWriteMultiple(ChannelOutboundBuffer in) throws Exception {
512         final long maxBytesPerGatheringWrite = config().getMaxBytesPerGatheringWrite();
513         IovArray array =  ((NativeArrays) registration().attachment()).cleanIovArray();
514         array.maxBytes(maxBytesPerGatheringWrite);
515         in.forEachFlushedMessage(array);
516 
517         if (array.count() >= 1) {
518             // TODO: Handle the case where cnt == 1 specially.
519             return writeBytesMultiple(in, array);
520         }
521         // cnt == 0, which means the outbound buffer contained empty buffers only.
522         in.removeBytes(0);
523         return 0;
524     }
525 
526     @Override
527     protected Object filterOutboundMessage(Object msg) {
528         if (msg instanceof ByteBuf) {
529             ByteBuf buf = (ByteBuf) msg;
530             return UnixChannelUtil.isBufferCopyNeededForWrite(buf)? newDirectBuffer(buf): buf;
531         }
532 
533         if (msg instanceof FileRegion || msg instanceof SpliceOutTask) {
534             return msg;
535         }
536 
537         throw new UnsupportedOperationException(
538                 "unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
539     }
540 
541     @Override
542     protected final void doShutdownOutput() throws Exception {
543         // The initial data that was sent by TCP_FASTOPEN_CONNECT must not be failed when shutting down the output.
544         removeFastOpenData();
545         socket.shutdown(false, true);
546     }
547 
548     private void shutdownInput0(final ChannelPromise promise) {
549         try {
550             socket.shutdown(true, false);
551             promise.setSuccess();
552         } catch (Throwable cause) {
553             promise.setFailure(cause);
554         }
555     }
556 
557     @Override
558     public boolean isOutputShutdown() {
559         return socket.isOutputShutdown();
560     }
561 
562     @Override
563     public boolean isInputShutdown() {
564         return socket.isInputShutdown();
565     }
566 
567     @Override
568     public boolean isShutdown() {
569         return socket.isShutdown();
570     }
571 
572     @Override
573     public ChannelFuture shutdownOutput() {
574         return shutdownOutput(newPromise());
575     }
576 
577     @Override
578     public ChannelFuture shutdownOutput(final ChannelPromise promise) {
579         EventLoop loop = eventLoop();
580         if (loop.inEventLoop()) {
581             ((AbstractUnsafe) unsafe()).shutdownOutput(promise);
582         } else {
583             loop.execute(new Runnable() {
584                 @Override
585                 public void run() {
586                     ((AbstractUnsafe) unsafe()).shutdownOutput(promise);
587                 }
588             });
589         }
590 
591         return promise;
592     }
593 
594     @Override
595     public ChannelFuture shutdownInput() {
596         return shutdownInput(newPromise());
597     }
598 
599     @Override
600     public ChannelFuture shutdownInput(final ChannelPromise promise) {
601         Executor closeExecutor = ((EpollStreamUnsafe) unsafe()).prepareToClose();
602         if (closeExecutor != null) {
603             closeExecutor.execute(new Runnable() {
604                 @Override
605                 public void run() {
606                     shutdownInput0(promise);
607                 }
608             });
609         } else {
610             EventLoop loop = eventLoop();
611             if (loop.inEventLoop()) {
612                 shutdownInput0(promise);
613             } else {
614                 loop.execute(new Runnable() {
615                     @Override
616                     public void run() {
617                         shutdownInput0(promise);
618                     }
619                 });
620             }
621         }
622         return promise;
623     }
624 
625     @Override
626     public ChannelFuture shutdown() {
627         return shutdown(newPromise());
628     }
629 
630     @Override
631     public ChannelFuture shutdown(final ChannelPromise promise) {
632         ChannelFuture shutdownOutputFuture = shutdownOutput();
633         if (shutdownOutputFuture.isDone()) {
634             shutdownOutputDone(shutdownOutputFuture, promise);
635         } else {
636             shutdownOutputFuture.addListener(new ChannelFutureListener() {
637                 @Override
638                 public void operationComplete(final ChannelFuture shutdownOutputFuture) throws Exception {
639                     shutdownOutputDone(shutdownOutputFuture, promise);
640                 }
641             });
642         }
643         return promise;
644     }
645 
646     private void shutdownOutputDone(final ChannelFuture shutdownOutputFuture, final ChannelPromise promise) {
647         ChannelFuture shutdownInputFuture = shutdownInput();
648         if (shutdownInputFuture.isDone()) {
649             shutdownDone(shutdownOutputFuture, shutdownInputFuture, promise);
650         } else {
651             shutdownInputFuture.addListener(new ChannelFutureListener() {
652                 @Override
653                 public void operationComplete(ChannelFuture shutdownInputFuture) throws Exception {
654                     shutdownDone(shutdownOutputFuture, shutdownInputFuture, promise);
655                 }
656             });
657         }
658     }
659 
660     private static void shutdownDone(ChannelFuture shutdownOutputFuture,
661                               ChannelFuture shutdownInputFuture,
662                               ChannelPromise promise) {
663         Throwable shutdownOutputCause = shutdownOutputFuture.cause();
664         Throwable shutdownInputCause = shutdownInputFuture.cause();
665         if (shutdownOutputCause != null) {
666             if (shutdownInputCause != null) {
667                 logger.debug("Exception suppressed because a previous exception occurred.",
668                         shutdownInputCause);
669             }
670             promise.setFailure(shutdownOutputCause);
671         } else if (shutdownInputCause != null) {
672             promise.setFailure(shutdownInputCause);
673         } else {
674             promise.setSuccess();
675         }
676     }
677 
678     @Override
679     protected void doClose() throws Exception {
680         try {
681             // Calling super.doClose() first so spliceTo(...) will fail on next call.
682             super.doClose();
683         } finally {
684             safeClosePipe(pipeIn);
685             safeClosePipe(pipeOut);
686             clearSpliceQueue(null);
687         }
688     }
689 
690     private void clearSpliceQueue(ClosedChannelException exception) {
691         Queue<SpliceInTask> sQueue = spliceQueue;
692         if (sQueue == null) {
693             return;
694         }
695         for (;;) {
696             SpliceInTask task = sQueue.poll();
697             if (task == null) {
698                 break;
699             }
700             if (exception == null) {
701                 exception = new ClosedChannelException();
702             }
703             task.promise.tryFailure(exception);
704         }
705     }
706 
707     private static void safeClosePipe(FileDescriptor fd) {
708         if (fd != null) {
709             try {
710                 fd.close();
711             } catch (IOException e) {
712                 logger.warn("Error while closing a pipe", e);
713             }
714         }
715     }
716 
717     class EpollStreamUnsafe extends AbstractEpollUnsafe {
718         // Overridden here just to be able to access this method from AbstractEpollStreamChannel
719         @Override
720         protected Executor prepareToClose() {
721             return super.prepareToClose();
722         }
723 
724         private void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf, Throwable cause,
725                                          boolean allDataRead, EpollRecvByteAllocatorHandle allocHandle) {
726             if (byteBuf != null) {
727                 if (byteBuf.isReadable()) {
728                     readPending = false;
729                     pipeline.fireChannelRead(byteBuf);
730                 } else {
731                     byteBuf.release();
732                 }
733             }
734             allocHandle.readComplete();
735             pipeline.fireChannelReadComplete();
736             pipeline.fireExceptionCaught(cause);
737 
738             // If oom will close the read event, release connection.
739             // See https://github.com/netty/netty/issues/10434
740             if (allDataRead ||
741                     cause instanceof OutOfMemoryError ||
742                     cause instanceof LeakPresenceDetector.AllocationProhibitedException ||
743                     cause instanceof IOException) {
744                 shutdownInput(true);
745             }
746         }
747 
748         @Override
749         EpollRecvByteAllocatorHandle newEpollHandle(RecvByteBufAllocator.ExtendedHandle handle) {
750             return new EpollRecvByteAllocatorStreamingHandle(handle);
751         }
752 
753         @Override
754         void epollInReady() {
755             final ChannelConfig config = config();
756             if (shouldBreakEpollInReady(config)) {
757                 clearEpollIn0();
758                 return;
759             }
760             final EpollRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
761             final ChannelPipeline pipeline = pipeline();
762             final ByteBufAllocator allocator = config.getAllocator();
763             allocHandle.reset(config);
764 
765             ByteBuf byteBuf = null;
766             boolean allDataRead = false;
767             Queue<SpliceInTask> sQueue = null;
768             try {
769                 do {
770                     if (sQueue != null || (sQueue = spliceQueue) != null) {
771                         SpliceInTask spliceTask = sQueue.peek();
772                         if (spliceTask != null) {
773                             boolean spliceInResult = spliceTask.spliceIn(allocHandle);
774 
775                             if (allocHandle.isReceivedRdHup()) {
776                                 shutdownInput(false);
777                             }
778                             if (spliceInResult) {
779                                 // We need to check if it is still active as if not we removed all SpliceTasks in
780                                 // doClose(...)
781                                 if (isActive()) {
782                                     sQueue.remove();
783                                 }
784                                 continue;
785                             } else {
786                                 break;
787                             }
788                         }
789                     }
790 
791                     // we use a direct buffer here as the native implementations only be able
792                     // to handle direct buffers.
793                     byteBuf = allocHandle.allocate(allocator);
794                     allocHandle.lastBytesRead(doReadBytes(byteBuf));
795                     if (allocHandle.lastBytesRead() <= 0) {
796                         // nothing was read, release the buffer.
797                         byteBuf.release();
798                         byteBuf = null;
799                         allDataRead = allocHandle.lastBytesRead() < 0;
800                         if (allDataRead) {
801                             // There is nothing left to read as we received an EOF.
802                             readPending = false;
803                         }
804                         break;
805                     }
806                     allocHandle.incMessagesRead(1);
807                     readPending = false;
808                     pipeline.fireChannelRead(byteBuf);
809                     byteBuf = null;
810 
811                     if (shouldBreakEpollInReady(config)) {
812                         // We need to do this for two reasons:
813                         //
814                         // - If the input was shutdown in between (which may be the case when the user did it in the
815                         //   fireChannelRead(...) method we should not try to read again to not produce any
816                         //   miss-leading exceptions.
817                         //
818                         // - If the user closes the channel we need to ensure we not try to read from it again as
819                         //   the filedescriptor may be re-used already by the OS if the system is handling a lot of
820                         //   concurrent connections and so needs a lot of filedescriptors. If not do this we risk
821                         //   reading data from a filedescriptor that belongs to another socket then the socket that
822                         //   was "wrapped" by this Channel implementation.
823                         break;
824                     }
825                 } while (allocHandle.continueReading());
826 
827                 allocHandle.readComplete();
828                 pipeline.fireChannelReadComplete();
829 
830                 if (allDataRead) {
831                     shutdownInput(true);
832                 }
833             } catch (Throwable t) {
834                 handleReadException(pipeline, byteBuf, t, allDataRead, allocHandle);
835             } finally {
836                 if (sQueue == null) {
837                     if (shouldStopReading(config)) {
838                         clearEpollIn();
839                     }
840                 } else {
841                     if (!config.isAutoRead()) {
842                         clearEpollIn();
843                     }
844                 }
845             }
846         }
847     }
848 
849     private void addToSpliceQueue(final SpliceInTask task) {
850         Queue<SpliceInTask> sQueue = spliceQueue;
851         if (sQueue == null) {
852             synchronized (this) {
853                 sQueue = spliceQueue;
854                 if (sQueue == null) {
855                     spliceQueue = sQueue = PlatformDependent.newMpscQueue();
856                 }
857             }
858         }
859         sQueue.add(task);
860     }
861 
862     protected abstract class SpliceInTask {
863         final ChannelPromise promise;
864         int len;
865 
866         protected SpliceInTask(int len, ChannelPromise promise) {
867             this.promise = promise;
868             this.len = len;
869         }
870 
871         abstract boolean spliceIn(RecvByteBufAllocator.Handle handle);
872 
873         protected final int spliceIn(FileDescriptor pipeOut, RecvByteBufAllocator.Handle handle) throws IOException {
874             // calculate the maximum amount of data we are allowed to splice
875             int length = Math.min(handle.guess(), len);
876             int splicedIn = 0;
877             for (;;) {
878                 // Splicing until there is nothing left to splice.
879                 int localSplicedIn = Native.splice(socket.intValue(), -1, pipeOut.intValue(), -1, length);
880                 handle.lastBytesRead(localSplicedIn);
881                 if (localSplicedIn == 0) {
882                     break;
883                 }
884                 splicedIn += localSplicedIn;
885                 length -= localSplicedIn;
886             }
887 
888             return splicedIn;
889         }
890     }
891 
892     // Let it directly implement channelFutureListener as well to reduce object creation.
893     private final class SpliceInChannelTask extends SpliceInTask implements ChannelFutureListener {
894         private final AbstractEpollStreamChannel ch;
895 
896         SpliceInChannelTask(AbstractEpollStreamChannel ch, int len, ChannelPromise promise) {
897             super(len, promise);
898             this.ch = ch;
899         }
900 
901         @Override
902         public void operationComplete(ChannelFuture future) throws Exception {
903             if (!future.isSuccess()) {
904                 // Use tryFailure(...) as the promise might already be closed by spliceTo(...)
905                 promise.tryFailure(future.cause());
906             }
907         }
908 
909         @Override
910         public boolean spliceIn(RecvByteBufAllocator.Handle handle) {
911             assert ch.eventLoop().inEventLoop();
912             if (len == 0) {
913                 // Use trySuccess() as the promise might already be closed by spliceTo(...)
914                 promise.trySuccess();
915                 return true;
916             }
917             try {
918                 // We create the pipe on the target channel as this will allow us to just handle pending writes
919                 // later in a correct fashion without get into any ordering issues when spliceTo(...) is called
920                 // on multiple Channels pointing to one target Channel.
921                 FileDescriptor pipeOut = ch.pipeOut;
922                 if (pipeOut == null) {
923                     // Create a new pipe as non was created before.
924                     FileDescriptor[] pipe = pipe();
925                     ch.pipeIn = pipe[0];
926                     pipeOut = ch.pipeOut = pipe[1];
927                 }
928 
929                 int splicedIn = spliceIn(pipeOut, handle);
930                 if (splicedIn > 0) {
931                     // Integer.MAX_VALUE is a special value which will result in splice forever.
932                     if (len != Integer.MAX_VALUE) {
933                         len -= splicedIn;
934                     }
935 
936                     // Depending on if we are done with splicing inbound data we set the right promise for the
937                     // outbound splicing.
938                     final ChannelPromise splicePromise;
939                     if (len == 0) {
940                         splicePromise = promise;
941                     } else {
942                         splicePromise = ch.newPromise().addListener(this);
943                     }
944 
945                     boolean autoRead = config().isAutoRead();
946 
947                     // Just call unsafe().write(...) and flush() as we not want to traverse the whole pipeline for this
948                     // case.
949                     ch.unsafe().write(new SpliceOutTask(ch, splicedIn, autoRead), splicePromise);
950                     ch.unsafe().flush();
951                     if (autoRead && !splicePromise.isDone()) {
952                         // Write was not done which means the target channel was not writable. In this case we need to
953                         // disable reading until we are done with splicing to the target channel because:
954                         //
955                         // - The user may want to trigger another splice operation once the splicing was complete.
956                         config().setAutoRead(false);
957                     }
958                 }
959 
960                 return len == 0;
961             } catch (Throwable cause) {
962                 // Use tryFailure(...) as the promise might already be closed by spliceTo(...)
963                 promise.tryFailure(cause);
964                 return true;
965             }
966         }
967     }
968 
969     private final class SpliceOutTask {
970         private final AbstractEpollStreamChannel ch;
971         private final boolean autoRead;
972         private int len;
973 
974         SpliceOutTask(AbstractEpollStreamChannel ch, int len, boolean autoRead) {
975             this.ch = ch;
976             this.len = len;
977             this.autoRead = autoRead;
978         }
979 
980         public boolean spliceOut() throws Exception {
981             assert ch.eventLoop().inEventLoop();
982             try {
983                 int splicedOut = Native.splice(ch.pipeIn.intValue(), -1, ch.socket.intValue(), -1, len);
984                 len -= splicedOut;
985                 if (len == 0) {
986                     if (autoRead) {
987                         // AutoRead was used and we spliced everything so start reading again
988                         config().setAutoRead(true);
989                     }
990                     return true;
991                 }
992                 return false;
993             } catch (IOException e) {
994                 if (autoRead) {
995                     // AutoRead was used and we spliced everything so start reading again
996                     config().setAutoRead(true);
997                 }
998                 throw e;
999             }
1000         }
1001     }
1002 
1003     private final class SpliceFdTask extends SpliceInTask {
1004         private final FileDescriptor fd;
1005         private final ChannelPromise promise;
1006         private int offset;
1007 
1008         SpliceFdTask(FileDescriptor fd, int offset, int len, ChannelPromise promise) {
1009             super(len, promise);
1010             this.fd = fd;
1011             this.promise = promise;
1012             this.offset = offset;
1013         }
1014 
1015         @Override
1016         public boolean spliceIn(RecvByteBufAllocator.Handle handle) {
1017             assert eventLoop().inEventLoop();
1018             if (len == 0) {
1019                 // Use trySuccess() as the promise might already be failed by spliceTo(...)
1020                 promise.trySuccess();
1021                 return true;
1022             }
1023 
1024             try {
1025                 FileDescriptor[] pipe = pipe();
1026                 FileDescriptor pipeIn = pipe[0];
1027                 FileDescriptor pipeOut = pipe[1];
1028                 try {
1029                     int splicedIn = spliceIn(pipeOut, handle);
1030                     if (splicedIn > 0) {
1031                         // Integer.MAX_VALUE is a special value which will result in splice forever.
1032                         if (len != Integer.MAX_VALUE) {
1033                             len -= splicedIn;
1034                         }
1035                         do {
1036                             int splicedOut = Native.splice(pipeIn.intValue(), -1, fd.intValue(), offset, splicedIn);
1037                             offset += splicedOut;
1038                             splicedIn -= splicedOut;
1039                         } while (splicedIn > 0);
1040                         if (len == 0) {
1041                             // Use trySuccess() as the promise might already be failed by spliceTo(...)
1042                             promise.trySuccess();
1043                             return true;
1044                         }
1045                     }
1046                     return false;
1047                 } finally {
1048                     safeClosePipe(pipeIn);
1049                     safeClosePipe(pipeOut);
1050                 }
1051             } catch (Throwable cause) {
1052                 // Use tryFailure(...) as the promise might already be failed by spliceTo(...)
1053                 promise.tryFailure(cause);
1054                 return true;
1055             }
1056         }
1057     }
1058 
1059     private final class EpollSocketWritableByteChannel extends SocketWritableByteChannel {
1060         EpollSocketWritableByteChannel() {
1061             super(socket);
1062             assert fd == socket;
1063         }
1064 
1065         @Override
1066         protected int write(final ByteBuffer buf, final int pos, final int limit) throws IOException {
1067             return socket.send(buf, pos, limit);
1068         }
1069 
1070         @Override
1071         protected ByteBufAllocator alloc() {
1072             return AbstractEpollStreamChannel.this.alloc();
1073         }
1074     }
1075 }