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