1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.channel.uring;
17
18 import io.netty.buffer.ByteBuf;
19 import io.netty.channel.Channel;
20 import io.netty.channel.ChannelException;
21 import io.netty.channel.ChannelFuture;
22 import io.netty.channel.ChannelFutureListener;
23 import io.netty.channel.ChannelMetadata;
24 import io.netty.channel.ChannelOutboundBuffer;
25 import io.netty.channel.ChannelPipeline;
26 import io.netty.channel.ChannelPromise;
27 import io.netty.channel.DefaultFileRegion;
28 import io.netty.channel.EventLoop;
29 import io.netty.channel.FileRegion;
30 import io.netty.channel.IoRegistration;
31 import io.netty.channel.socket.DuplexChannel;
32 import io.netty.channel.unix.IovArray;
33 import io.netty.util.ReferenceCounted;
34 import io.netty.util.internal.SystemPropertyUtil;
35 import io.netty.util.internal.logging.InternalLogger;
36 import io.netty.util.internal.logging.InternalLoggerFactory;
37
38 import java.io.IOException;
39 import java.net.SocketAddress;
40 import java.nio.ByteBuffer;
41 import java.nio.channels.WritableByteChannel;
42 import java.util.ArrayList;
43 import java.util.Collections;
44 import java.util.List;
45
46 import static io.netty.channel.unix.Errors.ioResult;
47
48 abstract class AbstractIoUringStreamChannel extends AbstractIoUringChannel implements DuplexChannel {
49 private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractIoUringStreamChannel.class);
50 private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
51
52
53
54
55
56
57
58 private static final int FILE_REGION_MAX_CHUNK_SIZE = Math.min(16 * 1024 * 1024,
59 Math.max(1, SystemPropertyUtil.getInt("io.netty.iouring.fileRegionChunkSize", 64 * 1024)));
60
61
62 byte writeOpCode;
63
64 long writeId;
65 byte readOpCode;
66 long readId;
67
68
69 private IoUringBufferRing bufferRing;
70
71 AbstractIoUringStreamChannel(Channel parent, LinuxSocket socket, boolean active) {
72 super(parent, socket, active);
73 }
74
75 AbstractIoUringStreamChannel(Channel parent, LinuxSocket socket, SocketAddress remote) {
76 super(parent, socket, remote);
77 }
78
79 @Override
80 protected final boolean isStreamSocket() {
81 return true;
82 }
83
84 @Override
85 public ChannelMetadata metadata() {
86 return METADATA;
87 }
88
89 @Override
90 protected AbstractUringUnsafe newUnsafe() {
91 return new IoUringStreamUnsafe();
92 }
93
94 @Override
95 public final ChannelFuture shutdown() {
96 return shutdown(newPromise());
97 }
98
99 @Override
100 public final ChannelFuture shutdown(final ChannelPromise promise) {
101 ChannelFuture shutdownOutputFuture = shutdownOutput();
102 if (shutdownOutputFuture.isDone()) {
103 shutdownOutputDone(shutdownOutputFuture, promise);
104 } else {
105 shutdownOutputFuture.addListener(new ChannelFutureListener() {
106 @Override
107 public void operationComplete(final ChannelFuture shutdownOutputFuture) throws Exception {
108 shutdownOutputDone(shutdownOutputFuture, promise);
109 }
110 });
111 }
112 return promise;
113 }
114
115 @Override
116 protected final void doShutdownOutput0() throws Exception {
117 socket.shutdown(false, true);
118 }
119
120 private void shutdownInput0(final ChannelPromise promise) {
121 try {
122 socket.shutdown(true, false);
123 promise.setSuccess();
124 } catch (Throwable cause) {
125 promise.setFailure(cause);
126 }
127 }
128
129 @Override
130 public final boolean isOutputShutdown() {
131 return socket.isOutputShutdown();
132 }
133
134 @Override
135 public final boolean isInputShutdown() {
136 return socket.isInputShutdown();
137 }
138
139 @Override
140 public final boolean isShutdown() {
141 return socket.isShutdown();
142 }
143
144 @Override
145 public final ChannelFuture shutdownOutput() {
146 return shutdownOutput(newPromise());
147 }
148
149 @Override
150 public final ChannelFuture shutdownOutput(final ChannelPromise promise) {
151 EventLoop loop = eventLoop();
152 if (loop.inEventLoop()) {
153 ((AbstractUnsafe) unsafe()).shutdownOutput(promise);
154 } else {
155 loop.execute(new Runnable() {
156 @Override
157 public void run() {
158 ((AbstractUnsafe) unsafe()).shutdownOutput(promise);
159 }
160 });
161 }
162
163 return promise;
164 }
165
166 @Override
167 public final ChannelFuture shutdownInput() {
168 return shutdownInput(newPromise());
169 }
170
171 @Override
172 public final ChannelFuture shutdownInput(final ChannelPromise promise) {
173 EventLoop loop = eventLoop();
174 if (loop.inEventLoop()) {
175 shutdownInput0(promise);
176 } else {
177 loop.execute(new Runnable() {
178 @Override
179 public void run() {
180 shutdownInput0(promise);
181 }
182 });
183 }
184 return promise;
185 }
186
187 private void shutdownOutputDone(final ChannelFuture shutdownOutputFuture, final ChannelPromise promise) {
188 ChannelFuture shutdownInputFuture = shutdownInput();
189 if (shutdownInputFuture.isDone()) {
190 shutdownDone(shutdownOutputFuture, shutdownInputFuture, promise);
191 } else {
192 shutdownInputFuture.addListener(new ChannelFutureListener() {
193 @Override
194 public void operationComplete(ChannelFuture shutdownInputFuture) throws Exception {
195 shutdownDone(shutdownOutputFuture, shutdownInputFuture, promise);
196 }
197 });
198 }
199 }
200
201 private static void shutdownDone(ChannelFuture shutdownOutputFuture,
202 ChannelFuture shutdownInputFuture,
203 ChannelPromise promise) {
204 Throwable shutdownOutputCause = shutdownOutputFuture.cause();
205 Throwable shutdownInputCause = shutdownInputFuture.cause();
206 if (shutdownOutputCause != null) {
207 if (shutdownInputCause != null) {
208 logger.info("Exception suppressed because a previous exception occurred.",
209 shutdownInputCause);
210 }
211 promise.setFailure(shutdownOutputCause);
212 } else if (shutdownInputCause != null) {
213 promise.setFailure(shutdownInputCause);
214 } else {
215 promise.setSuccess();
216 }
217 }
218
219 @Override
220 protected final void doRegister(ChannelPromise promise) {
221 ChannelPromise registerPromise = this.newPromise();
222
223 registerPromise.addListener(f -> {
224 if (f.isSuccess()) {
225 try {
226 short bgid = ((IoUringStreamChannelConfig) config()).getBufferGroupId();
227 if (bgid >= 0) {
228 final IoUringIoHandler ioUringIoHandler = registration().attachment();
229 bufferRing = ioUringIoHandler.findBufferRing(bgid);
230 }
231 if (active) {
232
233 schedulePollRdHup();
234 }
235 } finally {
236 promise.setSuccess();
237 }
238 } else {
239 promise.setFailure(f.cause());
240 }
241 });
242
243 super.doRegister(registerPromise);
244 }
245
246 @Override
247 protected Object filterOutboundMessage(Object msg) {
248 if (IoUring.isSpliceSupported() && msg instanceof DefaultFileRegion) {
249 return new IoUringFileRegion((DefaultFileRegion) msg);
250 }
251
252 if (msg instanceof FileRegion) {
253
254 return msg;
255 }
256
257 return super.filterOutboundMessage(msg);
258 }
259
260
261
262
263
264
265 static final class IovBufferCollector implements ChannelOutboundBuffer.MessageProcessor {
266 private List<ReferenceCounted> buffers;
267 private int remaining;
268
269 List<ReferenceCounted> collect(ChannelOutboundBuffer buffer, int count) {
270 if (count == 0) {
271 return Collections.emptyList();
272 }
273 ByteBuf first = (ByteBuf) buffer.current();
274
275 if (buffer.size() == 1 || count == 1 && first.isReadable()) {
276 return Collections.<ReferenceCounted>singletonList(first.retain());
277 }
278 buffers = new ArrayList<>(Math.min(count, buffer.size()));
279 remaining = count;
280 try {
281 buffer.forEachFlushedMessage(this);
282 return buffers;
283 } catch (Exception e) {
284 throw new IllegalStateException(e);
285 }
286 }
287
288 @Override
289 public boolean processMessage(Object msg) {
290 ByteBuf buf = (ByteBuf) msg;
291 if (buf.isReadable()) {
292 buffers.add(buf.retain());
293 remaining = remainingIovs(buf, remaining);
294 }
295 return remaining != 0;
296 }
297
298 static int remainingIovs(ByteBuf buf, int remaining) {
299 if (buf.nioBufferCount() == 1) {
300 return remaining - 1;
301 }
302 for (ByteBuffer nioBuffer : buf.nioBuffers(buf.readerIndex(), buf.readableBytes())) {
303 if (nioBuffer.remaining() != 0 && --remaining == 0) {
304 break;
305 }
306 }
307 return remaining;
308 }
309 }
310
311 protected class IoUringStreamUnsafe extends AbstractUringUnsafe {
312
313 private ByteBuf readBuffer;
314
315
316 private ByteBuf fileRegionChunkBuf;
317
318 @Override
319 protected int scheduleWriteMultiple(ChannelOutboundBuffer in) {
320 assert writeId == 0;
321
322 int fd = fd().intValue();
323 IoRegistration registration = registration();
324 IoUringIoHandler handler = registration.attachment();
325 IovArray iovArray = handler.iovArray();
326 int offset = iovArray.count();
327
328 try {
329 in.forEachFlushedMessage(filterWriteMultiple(iovArray));
330 } catch (Exception e) {
331
332 return scheduleWriteSingle(in.current());
333 }
334 long iovArrayAddress = iovArray.memoryAddress(offset);
335 int iovArrayLength = iovArray.count() - offset;
336
337 IoUringIoOps ops = IoUringIoOps.newWritev(fd, (byte) 0, 0, iovArrayAddress, iovArrayLength, nextOpsId());
338 byte opCode = ops.opcode();
339 writeId = submitWrite(ops);
340 writeOpCode = opCode;
341 if (writeId == 0) {
342 return 0;
343 }
344 return 1;
345 }
346
347 protected ChannelOutboundBuffer.MessageProcessor filterWriteMultiple(IovArray iovArray) {
348 return iovArray;
349 }
350
351 @Override
352 protected int scheduleWriteSingle(Object msg) {
353 assert writeId == 0;
354
355 int fd = fd().intValue();
356 IoRegistration registration = registration();
357 final IoUringIoOps ops;
358 if (msg instanceof IoUringFileRegion) {
359 IoUringFileRegion fileRegion = (IoUringFileRegion) msg;
360 try {
361 fileRegion.open();
362 } catch (IOException e) {
363 this.handleWriteError(e);
364 return 0;
365 }
366 ops = fileRegion.splice(fd);
367 } else if (msg instanceof FileRegion) {
368 return scheduleWriteFileRegion(fd, registration, (FileRegion) msg);
369 } else {
370 ByteBuf buf = (ByteBuf) msg;
371 long address = IoUring.memoryAddress(buf) + buf.readerIndex();
372 int length = buf.readableBytes();
373 ops = IoUringIoOps.newSend(fd, (byte) 0, 0, address, length, nextOpsId());
374 }
375 byte opCode = ops.opcode();
376 writeId = submitWrite(ops);
377 writeOpCode = opCode;
378 if (writeId == 0) {
379 return 0;
380 }
381 return 1;
382 }
383
384
385
386
387 private int scheduleWriteFileRegion(int fd, IoRegistration registration, FileRegion region) {
388 ByteBuf buf = fileRegionChunkBuf;
389 if (buf == null) {
390 long remaining = region.count() - region.transferred();
391 if (remaining > 0) {
392 int chunkSize = (int) Math.min(remaining, FILE_REGION_MAX_CHUNK_SIZE);
393 buf = alloc().directBuffer(chunkSize);
394 try {
395 ByteBufWritableByteChannel ch = new ByteBufWritableByteChannel(buf);
396
397
398
399
400 while (buf.writableBytes() > 0 && region.transferred() < region.count()) {
401 long t = region.transferTo(ch, region.transferred());
402 if (t <= 0) {
403 break;
404 }
405 }
406 if (buf.readableBytes() == 0) {
407 buf.release();
408 handleWriteError(new ChannelException(
409 "FileRegion.transferTo(...) produced 0 bytes (count="
410 + region.count() + ", transferred=" + region.transferred() + ')'));
411 return 0;
412 }
413 } catch (Exception e) {
414 buf.release();
415 handleWriteError(e);
416 return 0;
417 }
418 } else {
419
420
421 buf = alloc().directBuffer(0);
422 }
423 fileRegionChunkBuf = buf;
424 }
425 long address = IoUring.memoryAddress(buf) + buf.readerIndex();
426 int length = buf.readableBytes();
427 IoUringIoOps ops = IoUringIoOps.newSend(fd, (byte) 0, 0, address, length, nextOpsId());
428 try {
429 byte opCode = ops.opcode();
430 writeId = submitWrite(ops);
431 writeOpCode = opCode;
432 if (writeId == 0) {
433 return 0;
434 }
435 return 1;
436 } finally {
437 if (writeId == 0) {
438 releaseFileRegionChunkBuf();
439 }
440 }
441 }
442
443 @Override
444 List<ReferenceCounted> retainWriteBuffers(ChannelOutboundBuffer buffer) {
445 if (currentWrite.opcode() == Native.IORING_OP_WRITEV) {
446 return new IovBufferCollector().collect(buffer, currentWrite.len());
447 }
448 Object msg = buffer.current();
449
450 if (msg instanceof ByteBuf || msg instanceof IoUringFileRegion) {
451 return Collections.singletonList(((ReferenceCounted) msg).retain());
452 }
453 return Collections.emptyList();
454 }
455
456 private int calculateRecvFlags(boolean first) {
457
458
459
460
461
462
463 if (first) {
464 return 0;
465 }
466 return Native.MSG_DONTWAIT;
467 }
468
469 private short calculateRecvIoPrio(boolean first, boolean socketIsEmpty) {
470
471
472
473 if (first) {
474
475
476 return socketIsEmpty && IoUring.isCqeFSockNonEmptySupported() ?
477 Native.IORING_RECVSEND_POLL_FIRST : 0;
478 }
479 return 0;
480 }
481
482 @Override
483 protected int scheduleRead0(boolean first, boolean socketIsEmpty) {
484 assert readBuffer == null;
485 assert readId == 0 : readId;
486 final IoUringRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
487
488 if (bufferRing != null && bufferRing.isUsable()) {
489 return scheduleReadProviderBuffer(bufferRing, first, socketIsEmpty);
490 }
491
492
493 ByteBuf byteBuf = allocHandle.allocate(alloc());
494 try {
495 int fd = fd().intValue();
496 IoRegistration registration = registration();
497 short ioPrio = calculateRecvIoPrio(first, socketIsEmpty);
498 int recvFlags = calculateRecvFlags(first);
499 short opsId = nextOpsId();
500
501 IoUringIoOps ops = IoUringIoOps.newRecv(fd, (byte) 0, ioPrio, recvFlags,
502 IoUring.memoryAddress(byteBuf) + byteBuf.writerIndex(), byteBuf.writableBytes(), opsId);
503 readId = registration.submit(ops);
504 readOpCode = Native.IORING_OP_RECV;
505 if (readId == 0) {
506 return 0;
507 }
508 readBuffer = byteBuf;
509 byteBuf = null;
510 return 1;
511 } finally {
512 if (byteBuf != null) {
513 byteBuf.release();
514 }
515 }
516 }
517
518 private int scheduleReadProviderBuffer(IoUringBufferRing bufferRing, boolean first, boolean socketIsEmpty) {
519 short bgId = bufferRing.bufferGroupId();
520 try {
521 boolean multishot = IoUring.isRecvMultishotEnabled();
522 byte flags = (byte) Native.IOSQE_BUFFER_SELECT;
523 short ioPrio;
524 final int recvFlags;
525 if (multishot) {
526 ioPrio = Native.IORING_RECV_MULTISHOT;
527 recvFlags = 0;
528 } else {
529
530
531 ioPrio = calculateRecvIoPrio(first, socketIsEmpty);
532 recvFlags = calculateRecvFlags(first);
533 }
534 if (IoUring.isRecvsendBundleEnabled()) {
535
536
537 ioPrio |= Native.IORING_RECVSEND_BUNDLE;
538 }
539 IoRegistration registration = registration();
540 int fd = fd().intValue();
541 short opsId = nextOpsId();
542 IoUringIoOps ops = IoUringIoOps.newRecv(
543 fd, flags, ioPrio, recvFlags, 0,
544 0, opsId, bgId
545 );
546 readId = registration.submit(ops);
547 readOpCode = Native.IORING_OP_RECV;
548 if (readId == 0) {
549 return 0;
550 }
551 if (multishot) {
552
553 return -1;
554 }
555 return 1;
556 } catch (IllegalArgumentException illegalArgumentException) {
557 this.handleReadException(pipeline(), null, illegalArgumentException, false, recvBufAllocHandle());
558 return 0;
559 }
560 }
561
562 @Override
563 protected void readComplete0(byte op, int res, int flags, short data, int outstanding) {
564 ByteBuf byteBuf = readBuffer;
565 readBuffer = null;
566 if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
567 readId = 0;
568
569
570 if (byteBuf != null) {
571
572 byteBuf.release();
573 }
574 return;
575 }
576 boolean rearm = (flags & Native.IORING_CQE_F_MORE) == 0;
577 boolean useBufferRing = (flags & Native.IORING_CQE_F_BUFFER) != 0;
578 short bid = (short) (flags >> Native.IORING_CQE_BUFFER_SHIFT);
579 boolean more = (flags & Native.IORING_CQE_F_BUF_MORE) != 0;
580
581 boolean completeRead = shouldCompleteReadLoop(flags, isReadMultishot());
582 if (rearm) {
583
584 readId = 0;
585 }
586
587 boolean allDataRead = false;
588
589 final IoUringRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
590 final ChannelPipeline pipeline = pipeline();
591
592 try {
593 if (res < 0) {
594 if (res == Native.ERRNO_NOBUFS_NEGATIVE) {
595
596 if (!bufferRing.expand()) {
597
598
599
600 pipeline.fireUserEventTriggered(bufferRing.getExhaustedEvent());
601 }
602
603
604
605
606 scheduleRead(allocHandle.isFirstRead());
607 return;
608 }
609
610
611
612 allocHandle.lastBytesRead(ioResult("io_uring read", res));
613 } else if (res > 0) {
614 if (useBufferRing) {
615
616
617
618
619
620 int read = res;
621 for (;;) {
622 int attemptedBytesRead = bufferRing.attemptedBytesRead(bid);
623
624
625
626
627
628
629 int allocatedBuffers = bufferRing.allocatedBuffers();
630 byteBuf = bufferRing.useBuffer(bid, read, more);
631 read -= byteBuf.readableBytes();
632 allocHandle.attemptedBytesRead(attemptedBytesRead);
633 allocHandle.lastBytesRead(byteBuf.readableBytes());
634
635 assert read >= 0;
636 if (read == 0) {
637
638
639 break;
640 }
641 allocHandle.incMessagesRead(1);
642 pipeline.fireChannelRead(byteBuf);
643 byteBuf = null;
644 bid = bufferRing.nextBid(bid, allocatedBuffers);
645 if (!allocHandle.continueReading()) {
646
647 allocHandle.readComplete();
648 pipeline.fireChannelReadComplete();
649 allocHandle.reset(config());
650 }
651 }
652 } else {
653 int attemptedBytesRead = byteBuf.writableBytes();
654 byteBuf.writerIndex(byteBuf.writerIndex() + res);
655 allocHandle.attemptedBytesRead(attemptedBytesRead);
656 allocHandle.lastBytesRead(res);
657 }
658 } else {
659
660 allocHandle.lastBytesRead(-1);
661 }
662 if (allocHandle.lastBytesRead() <= 0) {
663
664 if (byteBuf != null) {
665
666 byteBuf.release();
667 byteBuf = null;
668 }
669 allDataRead = allocHandle.lastBytesRead() < 0;
670 if (allDataRead) {
671
672 shutdownInput(true);
673 }
674 allocHandle.readComplete();
675 pipeline.fireChannelReadComplete();
676 return;
677 }
678
679 allocHandle.incMessagesRead(1);
680 pipeline.fireChannelRead(byteBuf);
681 byteBuf = null;
682 scheduleNextRead(pipeline, allocHandle, rearm, completeRead);
683 } catch (Throwable t) {
684 handleReadException(pipeline, byteBuf, t, allDataRead, allocHandle);
685 }
686 }
687
688 private void scheduleNextRead(ChannelPipeline pipeline, IoUringRecvByteAllocatorHandle allocHandle,
689 boolean rearm, boolean completeRead) {
690 if (allocHandle.continueReading() && !completeRead) {
691 if (rearm) {
692
693
694 scheduleRead(false);
695 }
696 } else {
697
698 allocHandle.readComplete();
699 pipeline.fireChannelReadComplete();
700 }
701 }
702
703 protected final void handleReadException(ChannelPipeline pipeline, ByteBuf byteBuf,
704 Throwable cause, boolean allDataRead,
705 IoUringRecvByteAllocatorHandle allocHandle) {
706 if (byteBuf != null) {
707 if (byteBuf.isReadable()) {
708 pipeline.fireChannelRead(byteBuf);
709 } else {
710 byteBuf.release();
711 }
712 }
713 allocHandle.readComplete();
714 pipeline.fireChannelReadComplete();
715 pipeline.fireExceptionCaught(cause);
716 if (allDataRead || cause instanceof IOException) {
717 shutdownInput(true);
718 }
719 }
720
721 private boolean handleWriteCompleteFileRegion(ChannelOutboundBuffer channelOutboundBuffer,
722 IoUringFileRegion fileRegion, int res, short data) {
723 try {
724 if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
725 return true;
726 }
727 int result = res >= 0 ? res : ioResult("io_uring splice", res);
728 if (result == 0 && fileRegion.count() > 0) {
729 validateFileRegion(fileRegion.fileRegion, fileRegion.transfered());
730 return false;
731 }
732 int progress = fileRegion.handleResult(result, data);
733 if (progress == -1) {
734
735 channelOutboundBuffer.remove();
736 } else if (progress > 0) {
737 channelOutboundBuffer.progress(progress);
738 }
739 } catch (Throwable cause) {
740 handleWriteError(cause);
741 }
742 return true;
743 }
744
745 @Override
746 boolean writeComplete0(byte op, int res, int flags, long data, int outstanding) {
747 if ((flags & Native.IORING_CQE_F_NOTIF) == 0) {
748
749
750
751
752 writeId = 0;
753 writeOpCode = 0;
754 }
755 ChannelOutboundBuffer channelOutboundBuffer = unsafe().outboundBuffer();
756 if (channelOutboundBuffer == null) {
757
758 releaseFileRegionChunkBuf();
759 return true;
760 }
761 Object current = channelOutboundBuffer.current();
762 if (current instanceof IoUringFileRegion) {
763 IoUringFileRegion fileRegion = (IoUringFileRegion) current;
764
765 return handleWriteCompleteFileRegion(channelOutboundBuffer, fileRegion, res, (short) data);
766 }
767
768 if (current instanceof FileRegion) {
769 return handleWriteCompleteGenericFileRegion(
770 channelOutboundBuffer, (FileRegion) current, res);
771 }
772
773 if (res >= 0) {
774 channelOutboundBuffer.removeBytes(res);
775 } else if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
776 return true;
777 } else {
778 try {
779 if (ioResult("io_uring write", res) == 0) {
780 return false;
781 }
782 } catch (Throwable cause) {
783 handleWriteError(cause);
784 }
785 }
786 return true;
787 }
788
789
790
791
792
793 private boolean handleWriteCompleteGenericFileRegion(
794 ChannelOutboundBuffer channelOutboundBuffer, FileRegion region, int res) {
795 try {
796 if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
797 releaseFileRegionChunkBuf();
798 return true;
799 }
800 if (res >= 0) {
801 ByteBuf buf = fileRegionChunkBuf;
802 assert buf != null;
803 buf.skipBytes(res);
804 channelOutboundBuffer.progress(res);
805 if (!buf.isReadable()) {
806
807 releaseFileRegionChunkBuf();
808 if (region.transferred() >= region.count()) {
809 channelOutboundBuffer.remove();
810 }
811 } else {
812
813 return false;
814 }
815 } else {
816
817
818
819
820 if (ioResult("io_uring write", res) == 0) {
821 return false;
822 }
823 }
824 } catch (Throwable cause) {
825 releaseFileRegionChunkBuf();
826 handleWriteError(cause);
827 }
828 return true;
829 }
830
831 private void releaseFileRegionChunkBuf() {
832 if (fileRegionChunkBuf != null) {
833 fileRegionChunkBuf.release();
834 fileRegionChunkBuf = null;
835 }
836 }
837
838 @Override
839 public void unregistered() {
840 super.unregistered();
841 assert readBuffer == null;
842 releaseFileRegionChunkBuf();
843 }
844 }
845
846 @Override
847 protected final void cancelOutstandingReads(IoRegistration registration, int numOutstandingReads) {
848 if (readId != 0) {
849
850 assert numOutstandingReads == 1 || numOutstandingReads == -1;
851 IoUringIoOps ops = IoUringIoOps.newAsyncCancel((byte) 0, readId, readOpCode);
852 long id = registration.submit(ops);
853 assert id != 0;
854 readId = 0;
855 }
856 }
857
858 @Override
859 protected final void cancelOutstandingWrites(IoRegistration registration, int numOutstandingWrites) {
860 if (writeId != 0) {
861
862
863 assert numOutstandingWrites == 1;
864 assert writeOpCode != 0;
865 long id = registration.submit(IoUringIoOps.newAsyncCancel((byte) 0, writeId, writeOpCode));
866 assert id != 0;
867 writeId = 0;
868 }
869 }
870
871 @Override
872 protected boolean socketIsEmpty(int flags) {
873 return IoUring.isCqeFSockNonEmptySupported() && (flags & Native.IORING_CQE_F_SOCK_NONEMPTY) == 0;
874 }
875
876 protected boolean shouldCompleteReadLoop(int flags, boolean multishot) {
877 return socketIsEmpty(flags);
878 }
879
880 @Override
881 boolean isPollInFirst() {
882 return bufferRing == null || !bufferRing.isUsable();
883 }
884
885
886
887
888
889
890 private static final class ByteBufWritableByteChannel implements WritableByteChannel {
891 private final ByteBuf buf;
892
893 ByteBufWritableByteChannel(ByteBuf buf) {
894 this.buf = buf;
895 }
896
897 @Override
898 public int write(ByteBuffer src) {
899 int toWrite = Math.min(src.remaining(), buf.writableBytes());
900 if (toWrite == 0) {
901 return 0;
902 }
903 if (toWrite < src.remaining()) {
904 int oldLimit = src.limit();
905 src.limit(src.position() + toWrite);
906 buf.writeBytes(src);
907 src.limit(oldLimit);
908 return toWrite;
909 }
910 buf.writeBytes(src);
911 return toWrite;
912 }
913
914 @Override
915 public boolean isOpen() {
916 return true;
917 }
918
919 @Override
920 public void close() {
921
922 }
923 }
924 }