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.channel.IoHandle;
19 import io.netty.channel.IoHandler;
20 import io.netty.channel.IoHandlerContext;
21 import io.netty.channel.IoHandlerFactory;
22 import io.netty.channel.IoOps;
23 import io.netty.channel.IoRegistration;
24 import io.netty.channel.unix.Buffer;
25 import io.netty.channel.unix.Errors;
26 import io.netty.channel.unix.FileDescriptor;
27 import io.netty.channel.unix.IovArray;
28 import io.netty.util.collection.IntObjectHashMap;
29 import io.netty.util.collection.IntObjectMap;
30 import io.netty.util.concurrent.ThreadAwareExecutor;
31 import io.netty.util.internal.CleanableDirectBuffer;
32 import io.netty.util.internal.ObjectUtil;
33 import io.netty.util.internal.StringUtil;
34 import io.netty.util.internal.logging.InternalLogger;
35 import io.netty.util.internal.logging.InternalLoggerFactory;
36
37 import java.io.IOException;
38 import java.io.UncheckedIOException;
39 import java.nio.ByteBuffer;
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.concurrent.TimeUnit;
44 import java.util.concurrent.atomic.AtomicBoolean;
45 import java.util.concurrent.atomic.AtomicInteger;
46
47 import static java.lang.Math.max;
48 import static java.lang.Math.min;
49 import static java.util.Objects.requireNonNull;
50
51
52
53
54 public final class IoUringIoHandler implements IoHandler {
55 private static final InternalLogger logger = InternalLoggerFactory.getInstance(IoUringIoHandler.class);
56 private static final int WAKEUP_CLOSED = 1 << 30;
57
58 private final RingBuffer ringBuffer;
59 private final IntObjectMap<IoUringBufferRing> registeredIoUringBufferRing;
60 private final IntObjectMap<DefaultIoUringIoRegistration> registrations;
61
62 private final byte[] inet4AddressArray = new byte[SockaddrIn.IPV4_ADDRESS_LENGTH];
63 private final byte[] inet6AddressArray = new byte[SockaddrIn.IPV6_ADDRESS_LENGTH];
64
65 private final AtomicBoolean eventfdAsyncNotify = new AtomicBoolean();
66 private final AtomicInteger wakeupWriters = new AtomicInteger();
67 private final FileDescriptor eventfd;
68 private final CleanableDirectBuffer eventfdReadBufCleanable;
69 private final ByteBuffer eventfdReadBuf;
70 private final long eventfdReadBufAddress;
71 private final CleanableDirectBuffer timeoutMemoryCleanable;
72 private final ByteBuffer timeoutMemory;
73 private final long timeoutMemoryAddress;
74 private final IovArray iovArray;
75 private final IovArrayReferenceCollector iovArrayReferenceCollector;
76 private final MsgHdrMemoryArray msgHdrMemoryArray;
77 private long eventfdReadSubmitted;
78 private boolean eventFdClosing;
79 private volatile boolean shuttingDown;
80 private boolean closeCompleted;
81 private final PendingOpMap pendingOps;
82 private int nextRegistrationId = 1;
83
84 private static final long INVALID_ID = 0;
85 private static final long EVENTFD_TOKEN = PendingOpMap.token(1);
86 private static final long RINGFD_TOKEN = PendingOpMap.token(2);
87 private static final int KERNEL_TIMESPEC_SIZE = 16;
88
89 private static final int KERNEL_TIMESPEC_TV_SEC_FIELD = 0;
90 private static final int KERNEL_TIMESPEC_TV_NSEC_FIELD = 8;
91
92 private final ThreadAwareExecutor executor;
93
94
95 private final CompletionCallback completionCallback = this::handle;
96
97 IoUringIoHandler(ThreadAwareExecutor executor, IoUringIoHandlerConfig config) {
98
99 IoUring.ensureAvailability();
100 this.executor = requireNonNull(executor, "executor");
101 requireNonNull(config, "config");
102 int setupFlags = Native.setupFlags(config.singleIssuer());
103
104
105
106 int cqSize = 2 * config.getRingSize();
107 if (config.needSetupCqeSize()) {
108 assert IoUring.isSetupCqeSizeSupported();
109 setupFlags |= Native.IORING_SETUP_CQSIZE;
110 cqSize = config.getCqSize();
111 }
112 this.ringBuffer = Native.createRingBuffer(config.getRingSize(), cqSize, setupFlags);
113 if (IoUring.isRegisterIowqMaxWorkersSupported() && config.needRegisterIowqMaxWorker()) {
114 int maxBoundedWorker = Math.max(config.getMaxBoundedWorker(), 0);
115 int maxUnboundedWorker = Math.max(config.getMaxUnboundedWorker(), 0);
116 int result = Native.ioUringRegisterIoWqMaxWorkers(ringBuffer.fd(), maxBoundedWorker, maxUnboundedWorker);
117 if (result < 0) {
118
119 ringBuffer.close();
120 throw new UncheckedIOException(Errors.newIOException("io_uring_register", result));
121 }
122 }
123
124 registeredIoUringBufferRing = new IntObjectHashMap<>();
125 Collection<IoUringBufferRingConfig> bufferRingConfigs = config.getInternBufferRingConfigs();
126 if (bufferRingConfigs != null && !bufferRingConfigs.isEmpty()) {
127 for (IoUringBufferRingConfig bufferRingConfig : bufferRingConfigs) {
128 try {
129 IoUringBufferRing ring = newBufferRing(ringBuffer.fd(), bufferRingConfig);
130 registeredIoUringBufferRing.put(bufferRingConfig.bufferGroupId(), ring);
131 } catch (Errors.NativeIoException e) {
132 for (IoUringBufferRing bufferRing : registeredIoUringBufferRing.values()) {
133 bufferRing.close();
134 }
135
136 ringBuffer.close();
137 throw new UncheckedIOException(e);
138 }
139 }
140 }
141
142 registrations = new IntObjectHashMap<>();
143 pendingOps = new PendingOpMap(IoUring.DEFAULT_PENDING_OPS_INITIAL_CAPACITY);
144 eventfd = Native.newBlockingEventFd();
145 eventfdReadBufCleanable = Buffer.allocateDirectBufferWithNativeOrder(Long.BYTES);
146 eventfdReadBuf = eventfdReadBufCleanable.buffer();
147 eventfdReadBufAddress = Buffer.memoryAddress(eventfdReadBuf);
148 timeoutMemoryCleanable = Buffer.allocateDirectBufferWithNativeOrder(KERNEL_TIMESPEC_SIZE);
149 timeoutMemory = timeoutMemoryCleanable.buffer();
150 timeoutMemoryAddress = Buffer.memoryAddress(timeoutMemory);
151 iovArray = new IovArray(IoUring.NUM_ELEMENTS_IOVEC);
152 iovArrayReferenceCollector = new IovArrayReferenceCollector(iovArray);
153 msgHdrMemoryArray = new MsgHdrMemoryArray((short) 1024);
154 }
155
156 @Override
157 public void initialize() {
158 ringBuffer.enable();
159
160 for (IoUringBufferRing bufferRing : registeredIoUringBufferRing.values()) {
161 bufferRing.initialize();
162 }
163 }
164
165 @Override
166 public int run(IoHandlerContext context) {
167 if (closeCompleted) {
168 if (context.shouldReportActiveIoTime()) {
169 context.reportActiveIoTime(0);
170 }
171 return 0;
172 }
173 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
174 CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
175 if (!completionQueue.hasCompletions() && context.canBlock()) {
176 if (eventfdReadSubmitted == 0) {
177 submitEventFdRead();
178 }
179 long timeoutNanos = context.deadlineNanos() == -1 ? -1 : context.delayNanos(System.nanoTime());
180 submitAndWaitWithTimeout(submissionQueue, false, timeoutNanos);
181 } else {
182
183 submitAndClearNow(submissionQueue);
184 }
185
186 int ioCompletions;
187 if (context.shouldReportActiveIoTime()) {
188 long activeIoStartTimeNanos = System.nanoTime();
189 ioCompletions = processCompletionsAndHandleOverflow(
190 submissionQueue, completionQueue, completionCallback);
191 long activeIoEndTimeNanos = System.nanoTime();
192 context.reportActiveIoTime(activeIoEndTimeNanos - activeIoStartTimeNanos);
193 } else {
194 ioCompletions = processCompletionsAndHandleOverflow(
195 submissionQueue, completionQueue, completionCallback);
196 }
197 return ioCompletions;
198 }
199
200 int submitIfFullAndGetRemaining() {
201 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
202 if (submissionQueue.remaining() == 0) {
203 if (submitAndClearNow(submissionQueue) == 0) {
204 throw new IllegalStateException("Submission queue is full and no submissions were accepted");
205 }
206 }
207 return submissionQueue.remaining();
208 }
209
210 private boolean needSubmit(int sqFlags) {
211 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
212 return submissionQueue.count() > 0
213 || (sqFlags & (Native.IORING_SQ_CQ_OVERFLOW | Native.IORING_SQ_TASKRUN)) != 0;
214 }
215
216 private int processCompletionsAndHandleOverflow(SubmissionQueue submissionQueue, CompletionQueue completionQueue,
217 CompletionCallback callback) {
218 int ioCompletions = 0;
219 for (int i = 0; i < 128; i++) {
220 long packed = completionQueue.process(callback);
221 int total = (int) (packed >>> 32);
222 ioCompletions += (int) packed;
223 int sqFlags = submissionQueue.flags();
224 if ((sqFlags & Native.IORING_SQ_CQ_OVERFLOW) != 0) {
225 logger.warn("CompletionQueue overflow detected, consider increasing size: {} ",
226 completionQueue.ringEntries);
227 }
228 if (total == 0) {
229 if (!needSubmit(sqFlags)) {
230 break;
231 }
232 submitAndClearNow0(submissionQueue);
233 }
234 }
235 return ioCompletions;
236 }
237
238 private int submitAndClearNow(SubmissionQueue submissionQueue) {
239 if (needSubmit(submissionQueue.flags())) {
240 return submitAndClearNow0(submissionQueue);
241 }
242 return 0;
243 }
244
245 private int submitAndClearNow0(SubmissionQueue submissionQueue) {
246
247 int submitted = submissionQueue.submitAndGetNow();
248
249
250
251 iovArray.clear();
252 msgHdrMemoryArray.clear();
253 return submitted;
254 }
255
256 private static IoUringBufferRing newBufferRing(int ringFd, IoUringBufferRingConfig bufferRingConfig)
257 throws Errors.NativeIoException {
258 short bufferRingSize = bufferRingConfig.bufferRingSize();
259 short bufferGroupId = bufferRingConfig.bufferGroupId();
260 int flags = bufferRingConfig.isIncremental() ? Native.IOU_PBUF_RING_INC : 0;
261 long ioUringBufRingAddr = Native.ioUringRegisterBufRing(ringFd, bufferRingSize, bufferGroupId, flags);
262 if (ioUringBufRingAddr < 0) {
263 throw Errors.newIOException("ioUringRegisterBufRing", (int) ioUringBufRingAddr);
264 }
265 return new IoUringBufferRing(ringFd,
266 Buffer.wrapMemoryAddressWithNativeOrder(ioUringBufRingAddr, Native.ioUringBufRingSize(bufferRingSize)),
267 bufferRingSize, bufferRingConfig.batchSize(),
268 bufferGroupId, bufferRingConfig.isIncremental(), bufferRingConfig.allocator(),
269 bufferRingConfig.isBatchAllocation()
270 );
271 }
272
273 IoUringBufferRing findBufferRing(short bgId) {
274 IoUringBufferRing cached = registeredIoUringBufferRing.get(bgId);
275 if (cached != null) {
276 return cached;
277 }
278 throw new IllegalArgumentException(
279 String.format("Cant find bgId:%d, please register it in ioUringIoHandler", bgId)
280 );
281 }
282
283 private static void handleLoopException(Throwable throwable) {
284 logger.warn("Unexpected exception in the IO event loop.", throwable);
285
286
287
288 try {
289 Thread.sleep(100);
290 } catch (InterruptedException ignore) {
291
292 }
293 }
294
295 private boolean handle(int res, int flags, long udata, ByteBuffer extraCqeData) {
296 try {
297 if (udata == EVENTFD_TOKEN) {
298 handleEventFdRead();
299 return false;
300 }
301 if (udata == RINGFD_TOKEN) {
302 return false;
303 }
304 if (udata >= 0) {
305 handleFastPath(res, flags, udata, extraCqeData);
306 return true;
307 }
308 handleSlowPath(res, flags, udata, extraCqeData);
309 return true;
310 } catch (Error e) {
311 throw e;
312 } catch (Throwable throwable) {
313 handleLoopException(throwable);
314 return true;
315 }
316 }
317
318 private void handleFastPath(int res, int flags, long udata, ByteBuffer extraCqeData) {
319 int id = UserData.decodeId(udata);
320 byte op = UserData.decodeOp(udata);
321 long userData = UserData.decodeData(udata);
322 DefaultIoUringIoRegistration registration = registrations.get(id);
323 if (registration != null) {
324 traceCompletion(registration, id, op, res);
325 registration.handle(res, flags, op, userData, extraCqeData);
326 return;
327 }
328 if (logger.isDebugEnabled()) {
329 logger.debug("ignoring packed completion for unknown registration (registrationId={}, op={}, userData={},"
330 + " res={})",
331 id, Native.opToStr(op), userData, res);
332 }
333 }
334
335 private void handleSlowPath(int res, int flags, long udata, ByteBuffer extraCqeData) {
336 long sequence = PendingOpMap.tokenSequence(udata);
337 int slot = pendingOps.findSlot(udata);
338 if (slot != -1) {
339 int registrationId = pendingOps.registrationId(slot);
340 DefaultIoUringIoRegistration registration = registrations.get(registrationId);
341 byte op = pendingOps.op(slot);
342 long userData = pendingOps.userData(slot);
343
344
345 if ((flags & Native.IORING_CQE_F_MORE) == 0) {
346 pendingOps.release(slot);
347 }
348
349
350 if (registration != null) {
351 traceCompletion(registration, registrationId, op, res);
352 registration.handle(res, flags, op, userData, extraCqeData);
353 return;
354 }
355 if (logger.isDebugEnabled()) {
356 logger.debug("ignoring slow-path completion for missing registration (registrationId={}, seq={}, "
357 + "op={}, userData={}, res={})",
358 registrationId, sequence, Native.opToStr(op), userData, res);
359 }
360 return;
361 }
362 if (logger.isDebugEnabled()) {
363 logger.debug("ignoring slow-path completion for unknown sequence (seq={}, res={})", sequence, res);
364 }
365 }
366
367 private void traceCompletion(DefaultIoUringIoRegistration registration, int registrationId, byte op, int res) {
368 if (!logger.isTraceEnabled()) {
369 return;
370 }
371 int fd = registration.fd();
372 if (fd != -1) {
373 logger.trace("completed(ring {}): {}(fd={}, res={})",
374 ringBuffer.fd(), Native.opToStr(op), fd, res);
375 } else {
376 logger.trace("completed(ring {}): {}(registrationId={}, res={})",
377 ringBuffer.fd(), Native.opToStr(op), registrationId, res);
378 }
379 }
380
381 private void handleEventFdRead() {
382 eventfdReadSubmitted = 0;
383 if (!eventFdClosing) {
384 eventfdAsyncNotify.set(false);
385 submitEventFdRead();
386 }
387 }
388
389 private void submitEventFdRead() {
390 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
391 eventfdReadSubmitted = submissionQueue.addEventFdRead(
392 eventfd.intValue(), eventfdReadBufAddress, 0, 8, EVENTFD_TOKEN);
393 }
394
395 private int submitAndWaitWithTimeout(SubmissionQueue submissionQueue,
396 boolean linkTimeout, long timeoutNanoSeconds) {
397 if (timeoutNanoSeconds != -1) {
398
399
400 long seconds, nanoSeconds;
401 if (timeoutNanoSeconds == 0) {
402 seconds = 0;
403 nanoSeconds = 0;
404 } else {
405 seconds = (int) min(timeoutNanoSeconds / 1000000000L, Integer.MAX_VALUE);
406 nanoSeconds = (int) max(timeoutNanoSeconds - seconds * 1000000000L, 0);
407 }
408
409 timeoutMemory.putLong(KERNEL_TIMESPEC_TV_SEC_FIELD, seconds);
410 timeoutMemory.putLong(KERNEL_TIMESPEC_TV_NSEC_FIELD, nanoSeconds);
411 if (linkTimeout) {
412 submissionQueue.addLinkTimeout(timeoutMemoryAddress, RINGFD_TOKEN);
413 } else {
414 submissionQueue.addTimeout(timeoutMemoryAddress, RINGFD_TOKEN);
415 }
416 }
417 int submitted = submissionQueue.submitAndGet();
418
419
420 iovArray.clear();
421 msgHdrMemoryArray.clear();
422 return submitted;
423 }
424
425 @Override
426 public void prepareToDestroy() {
427 shuttingDown = true;
428 CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
429 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
430
431 List<DefaultIoUringIoRegistration> copy = new ArrayList<>(registrations.values());
432
433 for (DefaultIoUringIoRegistration registration: copy) {
434 registration.close();
435 }
436
437
438 Native.eventFdWrite(eventfd.intValue(), 1L);
439
440
441 submissionQueue.addNop((byte) Native.IOSQE_IO_DRAIN, RINGFD_TOKEN);
442
443
444 submissionQueue.submitAndGet();
445
446 while (completionQueue.hasCompletions()) {
447 processCompletionsAndHandleOverflow(submissionQueue, completionQueue, completionCallback);
448 if (submissionQueue.count() > 0) {
449 submissionQueue.submitAndGetNow();
450 }
451 }
452 }
453
454 @Override
455 public void destroy() {
456 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
457 CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
458 drainEventFd();
459 if (submissionQueue.remaining() < 2) {
460
461
462 submissionQueue.submit();
463 }
464
465
466
467
468
469
470 submissionQueue.addNop((byte) (Native.IOSQE_IO_DRAIN | Native.IOSQE_LINK), RINGFD_TOKEN);
471
472 submitAndWaitWithTimeout(submissionQueue, true, TimeUnit.MILLISECONDS.toNanos(200));
473 completionQueue.process(completionCallback);
474 for (IoUringBufferRing ioUringBufferRing : registeredIoUringBufferRing.values()) {
475 ioUringBufferRing.close();
476 }
477 completeRingClose();
478 }
479
480
481
482
483
484 private void drainEventFd() {
485 CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
486 SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
487 assert !eventFdClosing;
488 eventFdClosing = true;
489 boolean eventPending = eventfdAsyncNotify.getAndSet(true);
490 if (eventPending) {
491
492
493 while (eventfdReadSubmitted == 0) {
494 submitEventFdRead();
495 submissionQueue.submit();
496 }
497
498 class DrainFdEventCallback implements CompletionCallback {
499 boolean eventFdDrained;
500
501 @Override
502 public boolean handle(int res, int flags, long udata, ByteBuffer extraCqeData) {
503 if (udata == EVENTFD_TOKEN) {
504 eventFdDrained = true;
505 }
506 return IoUringIoHandler.this.handle(res, flags, udata, extraCqeData);
507 }
508 }
509 final DrainFdEventCallback handler = new DrainFdEventCallback();
510 completionQueue.process(handler);
511 while (!handler.eventFdDrained) {
512 submissionQueue.submitAndGet();
513 processCompletionsAndHandleOverflow(submissionQueue, completionQueue, handler);
514 }
515 }
516
517
518
519 if (eventfdReadSubmitted != 0) {
520 submissionQueue.addCancel(eventfdReadSubmitted, EVENTFD_TOKEN);
521 eventfdReadSubmitted = 0;
522 submissionQueue.submit();
523 }
524 }
525
526 private void completeRingClose() {
527 if (closeCompleted) {
528
529 return;
530 }
531 closeCompleted = true;
532 ringBuffer.close();
533 closeWakeupGate();
534 try {
535 eventfd.close();
536 } catch (IOException e) {
537 logger.warn("Failed to close eventfd", e);
538 }
539 eventfdReadBufCleanable.clean();
540 timeoutMemoryCleanable.clean();
541 iovArray.release();
542 msgHdrMemoryArray.release();
543 }
544
545 @Override
546 public IoRegistration register(IoHandle handle) throws Exception {
547 IoUringIoHandle ioHandle = cast(handle);
548 if (shuttingDown) {
549 throw new IllegalStateException("IoUringIoHandler is shutting down");
550 }
551 int startId = nextRegistrationId;
552 DefaultIoUringIoRegistration registration = new DefaultIoUringIoRegistration(executor, ioHandle);
553 for (;;) {
554 int id = nextRegistrationId();
555 DefaultIoUringIoRegistration old = registrations.put(id, registration);
556 if (old != null) {
557 assert old.handle != registration.handle;
558 registrations.put(id, old);
559 if (nextRegistrationId == startId) {
560 throw new IllegalStateException("registration id space exhausted");
561 }
562 } else {
563 registration.setId(id);
564 ioHandle.registered();
565 break;
566 }
567 }
568
569 return registration;
570 }
571
572 private int nextRegistrationId() {
573
574
575 int id = nextRegistrationId;
576 nextRegistrationId = id == Integer.MAX_VALUE ? 1 : id + 1;
577 return id;
578 }
579
580 private final class DefaultIoUringIoRegistration implements IoRegistration {
581 private final AtomicBoolean canceled = new AtomicBoolean();
582 private final ThreadAwareExecutor executor;
583 private final IoUringIoEvent event = new IoUringIoEvent(0, 0, (byte) 0, 0L);
584 final IoUringIoHandle handle;
585
586 private boolean removeLater;
587 private int outstandingCompletions;
588 private int id;
589
590 DefaultIoUringIoRegistration(ThreadAwareExecutor executor, IoUringIoHandle handle) {
591 this.executor = executor;
592 this.handle = handle;
593 }
594
595 void setId(int id) {
596 this.id = id;
597 }
598
599 @Override
600 public long submit(IoOps ops) {
601 IoUringIoOps ioOps = (IoUringIoOps) ops;
602 if (!isValid()) {
603 return INVALID_ID;
604 }
605 if ((ioOps.flags() & Native.IOSQE_CQE_SKIP_SUCCESS) != 0) {
606
607
608 throw new IllegalArgumentException("IOSQE_CQE_SKIP_SUCCESS not supported");
609 }
610 long userData = ioOps.userData();
611
612 if (canUseFastPath(userData)) {
613 long packedSeq = UserData.encode(id, ioOps.opcode(), (short) userData);
614 if (executor.isExecutorThread(Thread.currentThread())) {
615 submitFastPath0(ioOps, packedSeq);
616 } else {
617 executor.execute(() -> submitFastPath0(ioOps, packedSeq));
618 }
619 return packedSeq;
620 }
621 long token = pendingOps.nextToken();
622 if (executor.isExecutorThread(Thread.currentThread())) {
623 submitSlowPath0(ioOps, token, userData);
624 } else {
625 executor.execute(() -> submitSlowPath0(ioOps, token, userData));
626 }
627 return token;
628 }
629
630 private void submitFastPath0(IoUringIoOps ioOps, long seq) {
631 ringBuffer.ioUringSubmissionQueue().enqueueSqe(ioOps.opcode(), ioOps.flags(), ioOps.ioPrio(),
632 ioOps.fd(), ioOps.union1(), ioOps.union2(), ioOps.len(), ioOps.union3(), seq,
633 ioOps.union4(), ioOps.personality(), ioOps.union5(), ioOps.union6()
634 );
635 outstandingCompletions++;
636 }
637
638 private void submitSlowPath0(IoUringIoOps ioOps, long token, long userData) {
639 pendingOps.registerNormal(token, id, ioOps.opcode(), userData);
640 ringBuffer.ioUringSubmissionQueue().enqueueSqe(ioOps.opcode(), ioOps.flags(), ioOps.ioPrio(),
641 ioOps.fd(), ioOps.union1(), ioOps.union2(), ioOps.len(), ioOps.union3(), token,
642 ioOps.union4(), ioOps.personality(), ioOps.union5(), ioOps.union6()
643 );
644 outstandingCompletions++;
645 }
646
647 private boolean canUseFastPath(long userData) {
648 return ((short) userData) == userData;
649 }
650
651 private int fd() {
652 if (handle instanceof AbstractIoUringChannel) {
653 return ((AbstractIoUringChannel) handle).fd().intValue();
654 }
655 return -1;
656 }
657
658 @SuppressWarnings("unchecked")
659 @Override
660 public <T> T attachment() {
661 return (T) IoUringIoHandler.this;
662 }
663
664 @Override
665 public boolean isValid() {
666 return !canceled.get();
667 }
668
669 @Override
670 public boolean cancel() {
671 if (!canceled.compareAndSet(false, true)) {
672
673 return false;
674 }
675 if (executor.isExecutorThread(Thread.currentThread())) {
676 tryRemove();
677 } else {
678 executor.execute(this::tryRemove);
679 }
680 return true;
681 }
682
683 private void tryRemove() {
684 if (outstandingCompletions > 0) {
685
686
687 removeLater = true;
688 return;
689 }
690 remove();
691 }
692
693 private void remove() {
694 DefaultIoUringIoRegistration old = registrations.remove(id);
695 assert old == this;
696 handle.unregistered();
697 }
698
699 void close() {
700
701
702 assert executor.isExecutorThread(Thread.currentThread());
703 try {
704 handle.close();
705 } catch (Exception e) {
706 logger.debug("Exception during closing " + handle, e);
707 }
708 }
709
710 void handle(int res, int flags, byte op, long userData, ByteBuffer extraCqeData) {
711 event.update(res, flags, op, userData, extraCqeData);
712 handle.handle(this, event);
713
714
715 if ((flags & Native.IORING_CQE_F_MORE) == 0 && --outstandingCompletions == 0 && removeLater) {
716
717 removeLater = false;
718 remove();
719 }
720 }
721 }
722
723 private static IoUringIoHandle cast(IoHandle handle) {
724 if (handle instanceof IoUringIoHandle) {
725 return (IoUringIoHandle) handle;
726 }
727 throw new IllegalArgumentException("IoHandle of type " + StringUtil.simpleClassName(handle) + " not supported");
728 }
729
730 @Override
731 public void wakeup() {
732 if (!executor.isExecutorThread(Thread.currentThread()) &&
733 !eventfdAsyncNotify.getAndSet(true)) {
734
735
736
737
738 int s;
739 do {
740 s = wakeupWriters.get();
741 if ((s & WAKEUP_CLOSED) != 0) {
742 return;
743 }
744 } while (!wakeupWriters.compareAndSet(s, s + 1));
745 try {
746
747 Native.eventFdWrite(eventfd.intValue(), 1L);
748 } finally {
749 wakeupWriters.decrementAndGet();
750 }
751 }
752 }
753
754 private void closeWakeupGate() {
755 int s;
756 do {
757 s = wakeupWriters.get();
758 } while (!wakeupWriters.compareAndSet(s, s | WAKEUP_CLOSED));
759
760
761 while ((wakeupWriters.get() & ~WAKEUP_CLOSED) != 0) {
762 Thread.onSpinWait();
763 }
764 }
765
766 @Override
767 public boolean isCompatible(Class<? extends IoHandle> handleType) {
768 return IoUringIoHandle.class.isAssignableFrom(handleType);
769 }
770
771 IovArray iovArray() {
772 if (iovArray.isFull()) {
773
774 submitAndClearNow(ringBuffer.ioUringSubmissionQueue());
775 assert iovArray.count() == 0;
776 }
777 return iovArray;
778 }
779
780
781
782
783
784 IovArrayReferenceCollector iovArrayReferenceCollector() {
785 return iovArrayReferenceCollector;
786 }
787
788 MsgHdrMemoryArray msgHdrMemoryArray() {
789 if (msgHdrMemoryArray.isFull()) {
790
791 submitAndClearNow(ringBuffer.ioUringSubmissionQueue());
792 }
793 return msgHdrMemoryArray;
794 }
795
796
797
798
799 byte[] inet4AddressArray() {
800 return inet4AddressArray;
801 }
802
803
804
805
806 byte[] inet6AddressArray() {
807 return inet6AddressArray;
808 }
809
810
811
812
813
814
815 public static IoHandlerFactory newFactory() {
816 return newFactory(new IoUringIoHandlerConfig());
817 }
818
819
820
821
822
823
824
825
826 public static IoHandlerFactory newFactory(int ringSize) {
827 IoUringIoHandlerConfig configuration = new IoUringIoHandlerConfig();
828 configuration.setRingSize(ringSize);
829 return eventLoop -> new IoUringIoHandler(eventLoop, configuration);
830 }
831
832
833
834
835
836
837
838 public static IoHandlerFactory newFactory(IoUringIoHandlerConfig config) {
839 IoUring.ensureAvailability();
840 final IoUringIoHandlerConfig copy = ObjectUtil.checkNotNull(config, "config").verifyAndClone();
841 return new IoHandlerFactory() {
842 @Override
843 public IoHandler newHandler(ThreadAwareExecutor eventLoop) {
844 return new IoUringIoHandler(eventLoop, copy);
845 }
846
847 @Override
848 public boolean isChangingThreadSupported() {
849 return !copy.singleIssuer();
850 }
851 };
852 }
853 }