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.AddressedEnvelope;
20 import io.netty.channel.ChannelException;
21 import io.netty.channel.ChannelFuture;
22 import io.netty.channel.ChannelMetadata;
23 import io.netty.channel.ChannelOutboundBuffer;
24 import io.netty.channel.ChannelPipeline;
25 import io.netty.channel.ChannelPromise;
26 import io.netty.channel.DefaultAddressedEnvelope;
27 import io.netty.channel.IoRegistration;
28 import io.netty.channel.socket.DatagramChannel;
29 import io.netty.channel.socket.DatagramChannelConfig;
30 import io.netty.channel.socket.DatagramPacket;
31 import io.netty.channel.socket.SocketProtocolFamily;
32 import io.netty.channel.unix.Errors;
33 import io.netty.channel.unix.Errors.NativeIoException;
34 import io.netty.channel.unix.SegmentedDatagramPacket;
35 import io.netty.channel.unix.Socket;
36 import io.netty.util.ReferenceCountUtil;
37 import io.netty.util.ReferenceCounted;
38 import io.netty.util.UncheckedBooleanSupplier;
39 import io.netty.util.internal.ObjectUtil;
40 import io.netty.util.internal.StringUtil;
41 import io.netty.util.internal.SystemPropertyUtil;
42 import io.netty.util.internal.logging.InternalLogger;
43 import io.netty.util.internal.logging.InternalLoggerFactory;
44
45 import java.io.IOException;
46 import java.net.Inet4Address;
47 import java.net.InetAddress;
48 import java.net.InetSocketAddress;
49 import java.net.NetworkInterface;
50 import java.net.PortUnreachableException;
51 import java.net.SocketAddress;
52 import java.net.SocketException;
53 import java.nio.channels.UnresolvedAddressException;
54 import java.util.ArrayList;
55 import java.util.List;
56
57 import static io.netty.channel.unix.Errors.ioResult;
58
59 public final class IoUringDatagramChannel extends AbstractIoUringChannel implements DatagramChannel {
60 private static final InternalLogger logger = InternalLoggerFactory.getInstance(IoUringDatagramChannel.class);
61 private static final boolean IP_MULTICAST_ALL =
62 SystemPropertyUtil.getBoolean("io.netty.channel.iouring.ipMulticastAll", false);
63 private static final ChannelMetadata METADATA = new ChannelMetadata(true, 16);
64 private static final String EXPECTED_TYPES =
65 " (expected: " + StringUtil.simpleClassName(DatagramPacket.class) + ", " +
66 StringUtil.simpleClassName(AddressedEnvelope.class) + '<' +
67 StringUtil.simpleClassName(ByteBuf.class) + ", " +
68 StringUtil.simpleClassName(InetSocketAddress.class) + ">, " +
69 StringUtil.simpleClassName(ByteBuf.class) + ')';
70
71 private final IoUringDatagramChannelConfig config;
72 private volatile boolean connected;
73
74 static {
75 if (logger.isDebugEnabled()) {
76 logger.debug("-Dio.netty.channel.iouring.ipMulticastAll: {}", IP_MULTICAST_ALL);
77 }
78 }
79
80
81
82
83
84
85
86 private final MsgHdrMemoryArray recvmsgHdrs = new MsgHdrMemoryArray((short) 256);
87 private final MsgHdrMemoryArray sendmsgHdrs = new MsgHdrMemoryArray((short) 256);
88 private final int[] sendmsgResArray = new int[sendmsgHdrs.capacity()];
89
90
91
92
93
94 public IoUringDatagramChannel() {
95 this(null);
96 }
97
98
99
100
101
102 public IoUringDatagramChannel(SocketProtocolFamily family) {
103 this(LinuxSocket.newSocketDgram(useIpv6(family)), false);
104 }
105
106 private static boolean useIpv6(SocketProtocolFamily family) {
107 if (family == null) {
108 return Socket.isIPv6Preferred();
109 }
110 return family == SocketProtocolFamily.INET6;
111 }
112
113
114
115
116
117 public IoUringDatagramChannel(int fd) {
118 this(new LinuxSocket(fd), true);
119 }
120
121 private IoUringDatagramChannel(LinuxSocket fd, boolean active) {
122
123 super(null, fd, active);
124
125
126 try {
127 fd.setIpMulticastAll(IP_MULTICAST_ALL);
128 } catch (IOException | ChannelException e) {
129 logger.debug("Failed to set IP_MULTICAST_ALL to {}", IP_MULTICAST_ALL, e);
130 }
131
132 config = new IoUringDatagramChannelConfig(this);
133 }
134
135 @Override
136 protected boolean isStreamSocket() {
137 return false;
138 }
139
140 @Override
141 public InetSocketAddress remoteAddress() {
142 return (InetSocketAddress) super.remoteAddress();
143 }
144
145 @Override
146 public InetSocketAddress localAddress() {
147 return (InetSocketAddress) super.localAddress();
148 }
149
150 @Override
151 public ChannelMetadata metadata() {
152 return METADATA;
153 }
154
155 @Override
156 public boolean isActive() {
157 return socket.isOpen() && (config.getActiveOnOpen() && isRegistered() || super.isActive());
158 }
159
160 @Override
161 public boolean isConnected() {
162 return connected;
163 }
164
165 @Override
166 public ChannelFuture joinGroup(InetAddress multicastAddress) {
167 return joinGroup(multicastAddress, newPromise());
168 }
169
170 @Override
171 public ChannelFuture joinGroup(InetAddress multicastAddress, ChannelPromise promise) {
172 try {
173 return joinGroup(multicastAddress, defaultMulticastInterface(), null, promise);
174 } catch (IOException e) {
175 promise.setFailure(e);
176 }
177 return promise;
178 }
179
180
181
182
183
184 private NetworkInterface defaultMulticastInterface() throws SocketException {
185 NetworkInterface iface = config().getNetworkInterface();
186 if (iface == null) {
187 iface = NetworkInterface.getByInetAddress(localAddress().getAddress());
188 }
189 return iface;
190 }
191
192 @Override
193 public ChannelFuture joinGroup(
194 InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
195 return joinGroup(multicastAddress, networkInterface, newPromise());
196 }
197
198 @Override
199 public ChannelFuture joinGroup(
200 InetSocketAddress multicastAddress, NetworkInterface networkInterface,
201 ChannelPromise promise) {
202 return joinGroup(multicastAddress.getAddress(), networkInterface, null, promise);
203 }
204
205 @Override
206 public ChannelFuture joinGroup(
207 InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
208 return joinGroup(multicastAddress, networkInterface, source, newPromise());
209 }
210
211 @Override
212 public ChannelFuture joinGroup(
213 final InetAddress multicastAddress, final NetworkInterface networkInterface,
214 final InetAddress source, final ChannelPromise promise) {
215
216 ObjectUtil.checkNotNull(multicastAddress, "multicastAddress");
217 ObjectUtil.checkNotNull(networkInterface, "networkInterface");
218
219 try {
220 socket.joinGroup(multicastAddress, networkInterface, source);
221 promise.setSuccess();
222 } catch (IOException e) {
223 promise.setFailure(e);
224 }
225 return promise;
226 }
227
228 @Override
229 public ChannelFuture leaveGroup(InetAddress multicastAddress) {
230 return leaveGroup(multicastAddress, newPromise());
231 }
232
233 @Override
234 public ChannelFuture leaveGroup(InetAddress multicastAddress, ChannelPromise promise) {
235 try {
236 return leaveGroup(multicastAddress, defaultMulticastInterface(), null, promise);
237 } catch (IOException e) {
238 promise.setFailure(e);
239 }
240 return promise;
241 }
242
243 @Override
244 public ChannelFuture leaveGroup(
245 InetSocketAddress multicastAddress, NetworkInterface networkInterface) {
246 return leaveGroup(multicastAddress, networkInterface, newPromise());
247 }
248
249 @Override
250 public ChannelFuture leaveGroup(
251 InetSocketAddress multicastAddress,
252 NetworkInterface networkInterface, ChannelPromise promise) {
253 return leaveGroup(multicastAddress.getAddress(), networkInterface, null, promise);
254 }
255
256 @Override
257 public ChannelFuture leaveGroup(
258 InetAddress multicastAddress, NetworkInterface networkInterface, InetAddress source) {
259 return leaveGroup(multicastAddress, networkInterface, source, newPromise());
260 }
261
262 @Override
263 public ChannelFuture leaveGroup(
264 final InetAddress multicastAddress, final NetworkInterface networkInterface, final InetAddress source,
265 final ChannelPromise promise) {
266 ObjectUtil.checkNotNull(multicastAddress, "multicastAddress");
267 ObjectUtil.checkNotNull(networkInterface, "networkInterface");
268
269 try {
270 socket.leaveGroup(multicastAddress, networkInterface, source);
271 promise.setSuccess();
272 } catch (IOException e) {
273 promise.setFailure(e);
274 }
275 return promise;
276 }
277
278 @Override
279 public ChannelFuture block(
280 InetAddress multicastAddress, NetworkInterface networkInterface,
281 InetAddress sourceToBlock) {
282 return block(multicastAddress, networkInterface, sourceToBlock, newPromise());
283 }
284
285 @Override
286 public ChannelFuture block(
287 final InetAddress multicastAddress, final NetworkInterface networkInterface,
288 final InetAddress sourceToBlock, final ChannelPromise promise) {
289 ObjectUtil.checkNotNull(multicastAddress, "multicastAddress");
290 ObjectUtil.checkNotNull(sourceToBlock, "sourceToBlock");
291 ObjectUtil.checkNotNull(networkInterface, "networkInterface");
292
293 promise.setFailure(new UnsupportedOperationException("Multicast not supported"));
294 return promise;
295 }
296
297 @Override
298 public ChannelFuture block(InetAddress multicastAddress, InetAddress sourceToBlock) {
299 return block(multicastAddress, sourceToBlock, newPromise());
300 }
301
302 @Override
303 public ChannelFuture block(
304 InetAddress multicastAddress, InetAddress sourceToBlock, ChannelPromise promise) {
305 try {
306 return block(multicastAddress, defaultMulticastInterface(), sourceToBlock, promise);
307 } catch (Throwable e) {
308 promise.setFailure(e);
309 }
310 return promise;
311 }
312
313 @Override
314 protected AbstractUnsafe newUnsafe() {
315 return new IoUringDatagramChannelUnsafe();
316 }
317
318 @Override
319 protected void doBind(SocketAddress localAddress) throws Exception {
320 if (localAddress instanceof InetSocketAddress) {
321 InetSocketAddress socketAddress = (InetSocketAddress) localAddress;
322 if (socketAddress.getAddress().isAnyLocalAddress() &&
323 socketAddress.getAddress() instanceof Inet4Address) {
324 if (socket.family() == SocketProtocolFamily.INET6) {
325 localAddress = new InetSocketAddress(LinuxSocket.INET6_ANY, socketAddress.getPort());
326 }
327 }
328 }
329 super.doBind(localAddress);
330 active = true;
331 }
332
333 private static void checkUnresolved(AddressedEnvelope<?, ?> envelope) {
334 if (envelope.recipient() instanceof InetSocketAddress
335 && (((InetSocketAddress) envelope.recipient()).isUnresolved())) {
336 throw new UnresolvedAddressException();
337 }
338 }
339
340 @Override
341 protected Object filterOutboundMessage(Object msg) {
342 if (msg instanceof DatagramPacket) {
343 DatagramPacket packet = (DatagramPacket) msg;
344 checkUnresolved(packet);
345 ByteBuf content = packet.content();
346 return !content.hasMemoryAddress() ?
347 packet.replace(newDirectBuffer(packet, content)) : msg;
348 }
349
350 if (msg instanceof ByteBuf) {
351 ByteBuf buf = (ByteBuf) msg;
352 return !buf.hasMemoryAddress()? newDirectBuffer(buf) : buf;
353 }
354
355 if (msg instanceof AddressedEnvelope) {
356 @SuppressWarnings("unchecked")
357 AddressedEnvelope<Object, SocketAddress> e = (AddressedEnvelope<Object, SocketAddress>) msg;
358 checkUnresolved(e);
359 if (e.content() instanceof ByteBuf &&
360 (e.recipient() == null || e.recipient() instanceof InetSocketAddress)) {
361
362 ByteBuf content = (ByteBuf) e.content();
363 return !content.hasMemoryAddress()?
364 new DefaultAddressedEnvelope<>(
365 newDirectBuffer(e, content), (InetSocketAddress) e.recipient()) : e;
366 }
367 }
368
369 throw new UnsupportedOperationException(
370 "unsupported message type: " + StringUtil.simpleClassName(msg) + EXPECTED_TYPES);
371 }
372
373 @Override
374 public DatagramChannelConfig config() {
375 return config;
376 }
377
378 @Override
379 protected void doDisconnect() throws Exception {
380
381 socket.disconnect();
382 connected = active = false;
383
384 resetCachedAddresses();
385 }
386
387 @Override
388 protected void doClose() throws Exception {
389 super.doClose();
390 connected = false;
391 }
392
393 private final class IoUringDatagramChannelUnsafe extends AbstractUringUnsafe {
394 private final WriteProcessor writeProcessor = new WriteProcessor();
395
396 private ByteBuf readBuffer;
397
398 private final class WriteProcessor implements ChannelOutboundBuffer.MessageProcessor {
399 private int written;
400 @Override
401 public boolean processMessage(Object msg) {
402 if (scheduleWrite(msg, written == 0)) {
403 written++;
404 return true;
405 }
406 return false;
407 }
408
409 int write(ChannelOutboundBuffer in) {
410 written = 0;
411 try {
412 in.forEachFlushedMessage(this);
413 } catch (Exception e) {
414
415 throw new IllegalStateException(e);
416 }
417 return written;
418 }
419 }
420
421 @Override
422 protected void readComplete0(byte op, int res, int flags, short data, int outstanding) {
423 assert outstanding != -1 : "multi-shot not implemented yet";
424
425 final IoUringRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
426 final ChannelPipeline pipeline = pipeline();
427 ByteBuf byteBuf = this.readBuffer;
428 assert byteBuf != null;
429 MsgHdrMemory hdr = recvmsgHdrs.hdr(data);
430
431 recvmsgHdrs.setId(data, MsgHdrMemoryArray.NO_ID);
432
433 try {
434 if (res < 0) {
435 if (res != Native.ERRNO_ECANCELED_NEGATIVE) {
436
437
438 allocHandle.lastBytesRead(ioResult("io_uring recvmsg", res));
439 }
440 } else {
441 allocHandle.lastBytesRead(res);
442 if (hdr.hasPort(IoUringDatagramChannel.this)) {
443 allocHandle.incMessagesRead(1);
444 DatagramPacket packet = hdr.get(
445 IoUringDatagramChannel.this, registration().attachment(), byteBuf, res);
446 pipeline.fireChannelRead(packet);
447 }
448 }
449 } catch (Throwable t) {
450 Throwable e = (connected && t instanceof NativeIoException) ?
451 translateForConnected((NativeIoException) t) : t;
452 pipeline.fireExceptionCaught(e);
453 }
454
455 if (outstanding == 0) {
456
457
458 this.readBuffer.release();
459 this.readBuffer = null;
460 recvmsgHdrs.clear();
461
462 if (res != Native.ERRNO_ECANCELED_NEGATIVE) {
463 if (allocHandle.lastBytesRead() > 0 &&
464 allocHandle.continueReading(UncheckedBooleanSupplier.TRUE_SUPPLIER) &&
465
466
467
468
469 (!IoUring.isCqeFSockNonEmptySupported() ||
470 (flags & Native.IORING_CQE_F_SOCK_NONEMPTY) != 0)) {
471
472 scheduleRead(false);
473 } else {
474
475 allocHandle.readComplete();
476 pipeline.fireChannelReadComplete();
477 }
478 }
479 }
480 }
481
482 @Override
483 protected int scheduleRead0(boolean first, boolean socketIsEmpty) {
484 final IoUringRecvByteAllocatorHandle allocHandle = recvBufAllocHandle();
485 IoUringIoHandler ioUringIoHandler = registration().attachment();
486 int submissionQueueRemaining = ioUringIoHandler.submitIfFullAndGetRemaining();
487 ByteBuf byteBuf = allocHandle.allocate(alloc());
488 assert readBuffer == null;
489 readBuffer = byteBuf;
490
491 int writable = byteBuf.writableBytes();
492 allocHandle.attemptedBytesRead(writable);
493 int datagramSize = ((IoUringDatagramChannelConfig) config()).getMaxDatagramPayloadSize();
494
495 int numDatagram = datagramSize == 0 ? 1 : Math.max(1, byteBuf.writableBytes() / datagramSize);
496 numDatagram = Math.min(Math.min(submissionQueueRemaining, recvmsgHdrs.capacity()), numDatagram);
497 int scheduled = scheduleRecvmsg(byteBuf, numDatagram, datagramSize);
498 if (scheduled == 0) {
499
500
501 readBuffer = null;
502 byteBuf.release();
503 }
504 return scheduled;
505 }
506
507 private int scheduleRecvmsg(ByteBuf byteBuf, int numDatagram, int datagramSize) {
508 int writable = byteBuf.writableBytes();
509 long bufferAddress = IoUring.memoryAddress(byteBuf) + byteBuf.writerIndex();
510 if (numDatagram <= 1) {
511 return scheduleRecvmsg0(bufferAddress, writable, true, false) ? 1 : 0;
512 }
513 int i = 0;
514
515 for (; i < numDatagram && writable >= datagramSize; i++) {
516 if (!scheduleRecvmsg0(bufferAddress, datagramSize, i == 0, i + 1 < numDatagram)) {
517 break;
518 }
519 bufferAddress += datagramSize;
520 writable -= datagramSize;
521 }
522 return i;
523 }
524
525 private boolean scheduleRecvmsg0(long bufferAddress, int bufferLength, boolean first, boolean more) {
526 MsgHdrMemory msgHdrMemory = recvmsgHdrs.nextHdr();
527 if (msgHdrMemory == null) {
528
529 return false;
530 }
531 msgHdrMemory.set(socket, null, bufferAddress, bufferLength, (short) 0);
532
533 int fd = fd().intValue();
534 int msgFlags = first ? 0 : Native.MSG_DONTWAIT;
535 int sqeFlags = more ? Native.IOSQE_HARDLINK : 0;
536 IoRegistration registration = registration();
537
538
539 IoUringIoOps ops = IoUringIoOps.newRecvmsg(
540 fd, (byte) sqeFlags, msgFlags, msgHdrMemory.address(), msgHdrMemory.idx());
541 long id = registration.submit(ops);
542 if (id == 0) {
543
544 recvmsgHdrs.restoreNextHdr(msgHdrMemory);
545 return false;
546 }
547 recvmsgHdrs.setId(msgHdrMemory.idx(), id);
548 return true;
549 }
550
551 @Override
552 boolean writeComplete0(byte op, int res, int flags, long data, int outstanding) {
553
554
555 assert data >= 0 && data < sendmsgHdrs.capacity();
556 int idx = (int) data;
557
558 sendmsgHdrs.setId(idx, MsgHdrMemoryArray.NO_ID);
559 sendmsgResArray[idx] = res;
560
561 if (outstanding == 0) {
562
563 boolean writtenSomething = false;
564 int numWritten = sendmsgHdrs.length();
565 sendmsgHdrs.clear();
566 ChannelOutboundBuffer outboundBuffer = outboundBuffer();
567 if (retainedWriteBuffers != null) {
568
569 return true;
570 }
571 for (int i = 0; i < numWritten; i++) {
572 writtenSomething |= removeFromOutboundBuffer(
573 outboundBuffer, sendmsgResArray[i], "io_uring sendmsg");
574 }
575 return writtenSomething;
576 }
577 return true;
578 }
579
580 private boolean removeFromOutboundBuffer(ChannelOutboundBuffer outboundBuffer, int res, String errormsg) {
581 if (res >= 0) {
582
583 return outboundBuffer.remove();
584 }
585 if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
586 return false;
587 }
588 try {
589 return ioResult(errormsg, res) != 0;
590 } catch (Throwable cause) {
591 Throwable e = (connected && cause instanceof NativeIoException) ?
592 translateForConnected((NativeIoException) cause) : cause;
593 return outboundBuffer.remove(e);
594 }
595 }
596
597 @Override
598 void connectComplete(byte op, int res, int flags, long data) {
599 if (res >= 0) {
600 connected = true;
601 }
602 super.connectComplete(op, res, flags, data);
603 }
604
605 @Override
606 protected int scheduleWriteMultiple(ChannelOutboundBuffer in) {
607 return writeProcessor.write(in);
608 }
609
610 @Override
611 protected int scheduleWriteSingle(Object msg) {
612 return scheduleWrite(msg, true) ? 1 : 0;
613 }
614
615 private boolean scheduleWrite(Object msg, boolean first) {
616 final ByteBuf data;
617 final InetSocketAddress remoteAddress;
618 final int segmentSize;
619 if (msg instanceof AddressedEnvelope) {
620 @SuppressWarnings("unchecked")
621 AddressedEnvelope<ByteBuf, InetSocketAddress> envelope =
622 (AddressedEnvelope<ByteBuf, InetSocketAddress>) msg;
623 data = envelope.content();
624 remoteAddress = envelope.recipient();
625 if (msg instanceof SegmentedDatagramPacket) {
626 segmentSize = ((SegmentedDatagramPacket) msg).segmentSize();
627 } else {
628 segmentSize = 0;
629 }
630 } else {
631 data = (ByteBuf) msg;
632 remoteAddress = (InetSocketAddress) remoteAddress();
633 segmentSize = 0;
634 }
635
636 long bufferAddress = IoUring.memoryAddress(data) + data.readerIndex();
637 return scheduleSendmsg(remoteAddress, bufferAddress, data.readableBytes(), segmentSize, first);
638 }
639
640 private boolean scheduleSendmsg(InetSocketAddress remoteAddress, long bufferAddress,
641 int bufferLength, int segmentSize, boolean first) {
642 MsgHdrMemory hdr = sendmsgHdrs.nextHdr();
643 if (hdr == null) {
644
645
646 return false;
647 }
648 long id = 0;
649 try {
650 hdr.set(socket, remoteAddress, bufferAddress, bufferLength, (short) segmentSize);
651 int msgFlags = first ? 0 : Native.MSG_DONTWAIT;
652 IoUringIoOps ops = IoUringIoOps.newSendmsg(
653 fd().intValue(), (byte) 0, msgFlags, hdr.address(), hdr.idx());
654 id = submitWrite(ops);
655 if (id == 0) {
656 return false;
657 }
658 sendmsgHdrs.setId(hdr.idx(), id);
659 return true;
660 } finally {
661 if (id == 0) {
662 sendmsgHdrs.restoreNextHdr(hdr);
663 }
664 }
665 }
666
667 @Override
668 List<ReferenceCounted> retainWriteBuffers(ChannelOutboundBuffer buffer) {
669 final int count = (int) currentWrite.userData() + 1;
670 final List<ReferenceCounted> buffers = new ArrayList<>(count);
671 try {
672 buffer.forEachFlushedMessage(new ChannelOutboundBuffer.MessageProcessor() {
673 @Override
674 public boolean processMessage(Object msg) {
675 int index = buffers.size();
676 buffers.add(sendmsgHdrs.id(index) != MsgHdrMemoryArray.NO_ID ?
677 ((ReferenceCounted) msg).retain() : null);
678 return buffers.size() < count;
679 }
680 });
681 } catch (Exception e) {
682 throw new IllegalStateException(e);
683 }
684 return buffers;
685 }
686
687 @Override
688 void releaseWriteBuffers(List<ReferenceCounted> buffers, long data) {
689
690 ReferenceCountUtil.safeRelease(buffers.set((int) data, null));
691 }
692
693 @Override
694 public void unregistered() {
695 super.unregistered();
696 sendmsgHdrs.release();
697 recvmsgHdrs.release();
698 assert readBuffer == null;
699 }
700 }
701
702 private static IOException translateForConnected(NativeIoException e) {
703
704 if (e.expectedErr() == Errors.ERROR_ECONNREFUSED_NEGATIVE) {
705 PortUnreachableException error = new PortUnreachableException(e.getMessage());
706 error.initCause(e);
707 return error;
708 }
709 return e;
710 }
711
712
713
714
715
716
717 public static boolean isSegmentedDatagramPacketSupported() {
718 return IoUring.isAvailable();
719 }
720
721 @Override
722 protected void cancelOutstandingReads(IoRegistration registration, int numOutstandingReads) {
723 if (numOutstandingReads > 0) {
724 int canceled = cancel(registration, Native.IORING_OP_RECVMSG, recvmsgHdrs);
725 assert canceled == numOutstandingReads;
726 }
727 }
728
729 @Override
730 protected void cancelOutstandingWrites(IoRegistration registration, int numOutstandingWrites) {
731 if (numOutstandingWrites > 0) {
732 int canceled = cancel(registration, Native.IORING_OP_SENDMSG, sendmsgHdrs);
733 assert canceled == numOutstandingWrites;
734 }
735 }
736
737 private int cancel(IoRegistration registration, byte op, MsgHdrMemoryArray array) {
738 int cancelled = 0;
739 for (int idx = 0; idx < array.length(); idx++) {
740 long id = array.id(idx);
741 if (id == MsgHdrMemoryArray.NO_ID) {
742 continue;
743 }
744
745
746 IoUringIoOps ops = IoUringIoOps.newAsyncCancel((byte) 0, id, op);
747 registration.submit(ops);
748 cancelled++;
749 }
750 return cancelled;
751 }
752
753 @Override
754 protected boolean socketIsEmpty(int flags) {
755 return IoUring.isCqeFSockNonEmptySupported() && (flags & Native.IORING_CQE_F_SOCK_NONEMPTY) == 0;
756 }
757
758 @Override
759 boolean isPollInFirst() {
760 return false;
761 }
762 }