Repository: netty/netty
File: transport-classes-io_uring/src/main/java/io/netty/channel/uring/IoUringIoHandler.java (Java)

Code before:
        long udata = UserData.encode(RINGFD_ID, Native.IORING_OP_TIMEOUT, (short) 0);

        ringBuffer.ioUringSubmissionQueue().addTimeout(delayNanos, udata);
    }

    @Override
    public void prepareToDestroy() {
        shuttingDown = true;
        CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
        SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();

        List<DefaultIoUringIoRegistration> copy = new ArrayList<>(registrations.values());

        for (DefaultIoUringIoRegistration registration: copy) {
            registration.close();
        }

        // Ensure all previously submitted IOs get to complete before tearing down everything.
        long udata = UserData.encode(RINGFD_ID, Native.IORING_OP_NOP, (short) 0);
        submissionQueue.addNop((byte) Native.IOSQE_IO_DRAIN, udata);
        submissionQueue.submit();
        while (completionQueue.hasCompletions()) {
            completionQueue.process(this::handle);

            if (submissionQueue.count() > 0) {
                submissionQueue.submit();
            }
        }
    }

    @Override
    public void destroy() {
        SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();
        CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
        drainEventFd();
        if (submissionQueue.remaining() < 2) {
            // We need to submit 2 linked operations. Since they are linked, we cannot allow a submit-call to
            // separate them. We don't have enough room (< 2) in the queue, so we submit now to make more room.
            submissionQueue.submit();
        }
        // Try to drain all the IO from the queue first...
        long udata = UserData.encode(RINGFD_ID, Native.IORING_OP_NOP, (short) 0);
        submissionQueue.addNop((byte) Native.IOSQE_IO_DRAIN, udata);
        // ... but only wait for 200 milliseconds on this
        udata = UserData.encode(RINGFD_ID, Native.IORING_OP_LINK_TIMEOUT, (short) 0);
        submissionQueue.addLinkTimeout(TimeUnit.MILLISECONDS.toNanos(200), udata);
        submissionQueue.submitAndWait();
        completionQueue.process(this::handle);
        completeRingClose();
    }


Review comment (bug):
I was still happy to have the sub q to NOT have the address as part of the API since you can compose the timespec type as you wish, even in the wrong way  - but it's not a big deal actually

Code after:

        eventfdReadSubmitted = submissionQueue.addEventFdRead(
                eventfd.intValue(), eventfdReadBuf, 0, 8, udata);
    }

    private int submitAndWaitWithTimeout(SubmissionQueue submissionQueue,
                                         boolean linkTimeout, long timeoutNanoSeconds) {
        if (timeoutNanoSeconds != -1) {
            long udata = UserData.encode(RINGFD_ID,
                    linkTimeout ? Native.IORING_OP_LINK_TIMEOUT : Native.IORING_OP_TIMEOUT, (short) 0);
            // We use the same timespec pointer for all add*Timeout operations. This only works because we call
            // submit directly after it. This ensures the submitted timeout is considered "stable" and so can be reused.
            long seconds, nanoSeconds;
            if (timeoutNanoSeconds == 0) {
                seconds = 0;
                nanoSeconds = 0;
            } else {
                seconds = (int) min(timeoutNanoSeconds / 1000000000L, Integer.MAX_VALUE);
                nanoSeconds = (int) max(timeoutNanoSeconds - seconds * 1000000000L, 0);
            }

            PlatformDependent.putLong(timeoutMemoryAddress + KERNEL_TIMESPEC_TV_SEC_FIELD, seconds);
            PlatformDependent.putLong(timeoutMemoryAddress + KERNEL_TIMESPEC_TV_NSEC_FIELD, nanoSeconds);

            if (linkTimeout) {
                submissionQueue.addLinkTimeout(timeoutMemoryAddress, udata);
            } else {
                submissionQueue.addTimeout(timeoutMemoryAddress, udata);
            }
        }
        return submissionQueue.submitAndWait();
    }

    @Override
    public void prepareToDestroy() {
        shuttingDown = true;
        CompletionQueue completionQueue = ringBuffer.ioUringCompletionQueue();
        SubmissionQueue submissionQueue = ringBuffer.ioUringSubmissionQueue();

        List<DefaultIoUringIoRegistration> copy = new ArrayList<>(registrations.values());

        for (DefaultIoUringIoRegistration registration: copy) {
            registration.close();
        }

        // Ensure all previously submitted IOs get to complete before tearing down everything.
        long udata = UserData.encode(RINGFD_ID, Native.IORING_OP_NOP, (short) 0);
        submissionQueue.addNop((byte) Native.IOSQE_IO_DRAIN, udata);
        submissionQueue.submit();
        while (completionQueue.hasCompletions()) {
            completionQueue.process(this::handle);