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