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

Code before:
            acceptId = registration.submit(ops);
            if (acceptId == 0) {
                return 0;
            }
            if ((ioPrio & Native.IORING_ACCEPT_MULTISHOT) != 0) {
                // Let's return -1 to signal that we used multi-shot.
                return -1;
            }
            return 1;
        }

        @Override
        protected void readComplete0(byte op, int res, int flags, short data, int outstanding) {
            if (res == Native.ERRNO_ECANCELED_NEGATIVE) {
                acceptId = 0;
                return;
            }
            assert acceptId != 0;
            boolean rearm = (flags & Native.IORING_CQE_F_MORE) == 0;
            if (rearm) {
                // Only reset if we don't use multi-shot or we need to re-arm because the multi-shot was cancelled.
                acceptId = 0;
            }
            final IoUringRecvByteAllocatorHandle allocHandle =
                    (IoUringRecvByteAllocatorHandle) unsafe()
                            .recvBufAllocHandle();
            final ChannelPipeline pipeline = pipeline();
            allocHandle.lastBytesRead(res);

            if (res >= 0) {
                allocHandle.incMessagesRead(1);
                try {
                    Channel channel = newChildChannel(
                            res, acceptedAddressMemoryAddress, acceptedAddressLengthMemoryAddress);
                    pipeline.fireChannelRead(channel);

                    if (allocHandle.continueReading() &&
                            // If IORING_CQE_F_SOCK_NONEMPTY is supported we should check for it first before
                            // trying to schedule a read. If it's supported and not part of the flags we know for sure
                            // that the next read (which would be using Native.IORING_ACCEPT_DONTWAIT) will complete
                            // without be able to read any data. This is useless work and we can skip it.
                            //
                            // See https://github.com/axboe/liburing/wiki/What's-new-with-io_uring-in-6.10
                            !socketIsEmpty(flags)) {
                        if (rearm) {
                            // We only should schedule another read if we need to rearm.
                            // See https://github.com/axboe/liburing/wiki/io_uring-and-networking-in-2023#multi-shot
                            scheduleRead(false);
                        }
                    } else {
                        allocHandle.readComplete();

Review comment (suggestion):
"Note that for the multishot variants, setting addr and addrlen may not make a lot of sense, as the same value would be used for every accepted connection. This means that the data written to addr may be overwritten by a new connection before the application has had time to process a past connection"

https://man7.org/linux/man-pages/man3/io_uring_prep_multishot_accept_direct.3.html

so we should call `getpeername` in readComplete0 to get real peer address

Code after:
            //
            // Depending on if this is the first read or not we will use Native.IORING_ACCEPT_DONT_WAIT.
            // The idea is that if the socket is blocking we can do the first read in a blocking fashion
            // and so not need to also register POLLIN. As we can not 100 % sure if reads after the first will
            // be possible directly we schedule these with Native.IORING_ACCEPT_DONT_WAIT. This allows us to still be
            // able to signal the fireChannelReadComplete() in a timely manner and be consistent with other
            // transports.
            short ioPrio;

            // IORING_ACCEPT_POLL_FIRST and IORING_ACCEPT_DONTWAIT were added in the same release.
            // We need to check if its supported as otherwise providing these would result in an -EINVAL.
            if (IoUring.isIOUringAcceptNoWaitSupported()) {
                if (first) {
                    ioPrio = socketIsEmpty ? Native.IORING_ACCEPT_POLL_FIRST : 0;
                } else {
                    ioPrio = Native.IORING_ACCEPT_DONTWAIT;
                }
            } else {
                ioPrio = 0;
            }

            final long acceptedAddressMemoryAddress;
            final long acceptedAddressLengthMemoryAddress;
            if (IoUring.isIOUringAcceptMultishotSupported()) {
                // Let's use multi-shot accept to reduce overhead.
                ioPrio |= Native.IORING_ACCEPT_MULTISHOT;
                acceptedAddressMemoryAddress = 0;
                acceptedAddressLengthMemoryAddress = 0;
            } else {
                assert acceptedAddressMemory != null;
                acceptedAddressMemoryAddress = acceptedAddressMemory.acceptedAddressMemoryAddress;
                acceptedAddressLengthMemoryAddress = acceptedAddressMemory.acceptedAddressLengthMemoryAddress;
            }

            // See https://github.com/axboe/liburing/wiki/What's-new-with-io_uring-in-6.10#improvements-for-accept
            IoUringIoOps ops = IoUringIoOps.newAccept(fd, flags((byte) 0), 0, ioPrio,
                    acceptedAddressMemoryAddress, acceptedAddressLengthMemoryAddress, nextOpsId());
            acceptId = registration.submit(ops);
            if (acceptId == 0) {
                return 0;
            }
            if ((ioPrio & Native.IORING_ACCEPT_MULTISHOT) != 0) {
                // Let's return -1 to signal that we used multi-shot.
                return -1;
            }
            return 1;
        }

        @Override
        protected void readComplete0(byte op, int res, int flags, short data, int outstanding) {
            if (res == Native.ERRNO_ECANCELED_NEGATIVE) {