1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
70
71 ((AbstractEpollUnsafe) unsafe()).flush0();
72 }
73 };
74
75
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
96 super(parent, fd, true, EpollIoOps.EPOLLRDHUP);
97 }
98
99 protected AbstractEpollStreamChannel(Channel parent, LinuxSocket fd, SocketAddress remote) {
100
101 super(parent, fd, remote, EpollIoOps.EPOLLRDHUP);
102 }
103
104 protected AbstractEpollStreamChannel(LinuxSocket fd, boolean active) {
105
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
121
122
123
124
125
126
127
128
129
130
131
132
133 @Deprecated
134 public final ChannelFuture spliceTo(final AbstractEpollStreamChannel ch, final int len) {
135 return spliceTo(ch, len, newPromise());
136 }
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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
175
176
177
178
179
180
181
182
183
184
185
186
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
195
196
197
198
199
200
201
202
203
204
205
206
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
229
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
237 clearSpliceQueue(ex);
238 }
239 });
240 }
241 }
242 }
243 }
244
245
246
247
248
249
250
251
252
253
254
255
256
257
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
277
278
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
290
291
292
293
294
295
296
297
298
299
300
301
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
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
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
355
356
357
358
359
360
361
362
363
364
365
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
390
391
392
393
394
395
396
397
398
399
400
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
425 removeFastOpenData();
426 int writeSpinCount = config().getWriteSpinCount();
427 do {
428 final int msgCount = in.size();
429
430 if (msgCount > 1 && in.current() instanceof ByteBuf) {
431 writeSpinCount -= doWriteMultiple(in);
432 } else if (msgCount == 0) {
433
434 clearFlag(Native.EPOLLOUT);
435
436 return;
437 } else {
438 writeSpinCount -= doWriteSingle(in);
439 }
440
441
442
443
444 } while (writeSpinCount > 0);
445
446 if (writeSpinCount == 0) {
447
448
449
450
451 clearFlag(Native.EPOLLOUT);
452
453
454 eventLoop().execute(flushTask);
455 } else {
456
457
458 setFlag(Native.EPOLLOUT);
459 }
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476 protected int doWriteSingle(ChannelOutboundBuffer in) throws Exception {
477
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
493 throw new Error("Unexpected message type: " + className(msg));
494 }
495 }
496
497
498
499
500
501
502
503
504
505
506
507
508
509
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
519 return writeBytesMultiple(in, array);
520 }
521
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
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
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
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
739
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
780
781 if (isActive()) {
782 sQueue.remove();
783 }
784 continue;
785 } else {
786 break;
787 }
788 }
789 }
790
791
792
793 byteBuf = allocHandle.allocate(allocator);
794 allocHandle.lastBytesRead(doReadBytes(byteBuf));
795 if (allocHandle.lastBytesRead() <= 0) {
796
797 byteBuf.release();
798 byteBuf = null;
799 allDataRead = allocHandle.lastBytesRead() < 0;
800 if (allDataRead) {
801
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
813
814
815
816
817
818
819
820
821
822
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
875 int length = Math.min(handle.guess(), len);
876 int splicedIn = 0;
877 for (;;) {
878
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
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
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
914 promise.trySuccess();
915 return true;
916 }
917 try {
918
919
920
921 FileDescriptor pipeOut = ch.pipeOut;
922 if (pipeOut == null) {
923
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
932 if (len != Integer.MAX_VALUE) {
933 len -= splicedIn;
934 }
935
936
937
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
948
949 ch.unsafe().write(new SpliceOutTask(ch, splicedIn, autoRead), splicePromise);
950 ch.unsafe().flush();
951 if (autoRead && !splicePromise.isDone()) {
952
953
954
955
956 config().setAutoRead(false);
957 }
958 }
959
960 return len == 0;
961 } catch (Throwable cause) {
962
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
988 config().setAutoRead(true);
989 }
990 return true;
991 }
992 return false;
993 } catch (IOException e) {
994 if (autoRead) {
995
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
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
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
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
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 }