Repository: tokio-rs/tokio
File: tokio/src/runtime/io/driver/uring.rs (Rust)

Code before:
    }

    /// Check if the io_uring context is initialized. If not, it will try to initialize it.
    /// Then, check if the provided opcode is supported.
    ///
    /// If both the context initialization succeeds and the opcode is supported,
    /// this returns `Ok(true)`.
    /// If either io_uring is unsupported or the opcode is unsupported,
    /// this returns `Ok(false)`.
    /// An error is returned if an io_uring syscall returns an unexpected error value.
    pub(crate) fn check_and_init(&self, opcode: u8) -> io::Result<bool> {
        match State::from_usize(self.uring_state.load(Ordering::Acquire)) {
            State::Uninitialized => match self.try_init_and_check_opcode(opcode) {
                Ok(opcode_supported) => {
                    self.set_uring_state(State::Initialized);
                    Ok(opcode_supported)
                }
                // If the system doesn't support io_uring, we set the state to Unsupported.
                Err(e) if e.raw_os_error() == Some(libc::ENOSYS) => {
                    self.set_uring_state(State::Unsupported);
                    Ok(false)
                }
                // If we get EPERM, io-uring syscalls may be blocked (for example, by seccomp).
                // In this case, we try to fall back to spawn_blocking for this and future operations.
                // See also: https://github.com/tokio-rs/tokio/issues/7691
                Err(e) if e.raw_os_error() == Some(libc::EPERM) => {
                    self.set_uring_state(State::Unsupported);
                    Ok(false)
                }
                // For other system errors, we just return it.
                Err(e) => Err(e),
            },
            State::Unsupported => Ok(false),
            State::Initialized => Ok(self.get_uring().lock().is_opcode_supported(opcode)),
        }
    }

    /// Initialize the io_uring context if it hasn't been initialized yet.
    /// Then, check whether the given opcode is supported.
    fn try_init_and_check_opcode(&self, opcode: u8) -> io::Result<bool> {
        let mut guard = self.get_uring().lock();
        if guard.try_init()? {
            self.add_uring_source(guard.ring().as_raw_fd())?;
        }

        Ok(guard.is_opcode_supported(opcode))
    }

    /// Register an operation with the io_uring.
    ///
    /// If this is the first io_uring operation, it will also initialize the io_uring context.

Review comment (suggestion):
If you want to preserve the opcode check, you can access the `OnceCell` without async using e.g. `get`.

Code after:
            .as_ref()
            .is_some_and(|probe| probe.is_supported(opcode)))
    }

    /// Initialize the io_uring context if it hasn't been initialized yet.
    fn try_init(&self, probe: &mut Probe) -> io::Result<()> {
        let mut guard = self.get_uring().lock();
        if guard.try_init(probe)? {
            self.add_uring_source(guard.ring().as_raw_fd())?;
        }

        Ok(())
    }

    /// Register an operation with the io_uring.
    ///
    /// If this is the first io_uring operation, it will also initialize the io_uring context.
    /// If io_uring isn't supported, this function returns an `ENOSYS` error, so the caller can
    /// perform custom handling, such as falling back to an alternative mechanism.
    ///
    /// # Safety
    ///
    /// Callers must ensure that parameters of the entry (such as buffer) are valid and will
    /// be valid for the entire duration of the operation, otherwise it may cause memory problems.
    pub(crate) unsafe fn register_op(&self, entry: Entry, waker: Waker) -> io::Result<usize> {
        assert!(self.uring_probe.initialized());

        // Uring is initialized.

        let mut guard = self.get_uring().lock();
        let ctx = &mut *guard;
        let index = ctx.ops.insert(Lifecycle::Waiting(waker));
        let entry = entry.user_data(index as u64);

        let submit_or_remove = |ctx: &mut UringContext| -> io::Result<()> {
            if let Err(e) = ctx.submit() {
                // Submission failed, remove the entry from the slab and return the error
                ctx.remove_op(index);
                return Err(e);
            }
            Ok(())
        };

        // SAFETY: entry is valid for the entire duration of the operation
        while unsafe { ctx.ring_mut().submission().push(&entry).is_err() } {
            // If the submission queue is full, flush it to the kernel
            submit_or_remove(ctx)?;
        }

        // Ensure that the completion queue is not full before submitting the entry.
        while ctx.ring_mut().completion().is_full() {