tokio-uring-xitca 0.2.0

a fork of tokio-uring with miniaml maintenance
Documentation
//! Tokio-uring provides a safe [io-uring] interface for the Tokio runtime. The
//! library requires Linux kernel 5.10 or later.
//!
//! [io-uring]: https://kernel.dk/io_uring.pdf
//!
//! # Getting started
//!
//! Using `tokio-uring` requires starting a [`tokio-uring`] runtime. This
//! runtime internally manages the main Tokio runtime and a `io-uring` driver.
//!
//! ```no_run
//! use tokio_uring_xitca::fs::File;
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//!     tokio_uring_xitca::start(async {
//!         // Open a file
//!         let file = File::open("hello.txt").await?;
//!
//!         let buf = vec![0; 4096];
//!         // Read some data, the buffer is passed by ownership and
//!         // submitted to the kernel. When the operation completes,
//!         // we get the buffer back.
//!         let (res, buf) = file.read_at(buf, 0).await;
//!         let n = res?;
//!
//!         // Display the contents
//!         println!("{:?}", &buf[..n]);
//!
//!         Ok(())
//!     })
//! }
//! ```
//!
//! Under the hood, `tokio_uring_xitca::start` starts a [`current-thread`] Runtime.
//! For concurrency, spawn multiple threads, each with a `tokio-uring` runtime.
//! The `tokio-uring` resource types are optimized for single-threaded usage and
//! most are `!Sync`.
//!
//! # Submit-based operations
//!
//! Unlike Tokio proper, `io-uring` is based on submission based operations.
//! Ownership of resources are passed to the kernel, which then performs the
//! operation. When the operation completes, ownership is passed back to the
//! caller. Because of this difference, the `tokio-uring` APIs diverge.
//!
//! For example, in the above example, reading from a `File` requires passing
//! ownership of the buffer.
//!
//! # Closing resources
//!
//! With `io-uring`, closing a resource (e.g. a file) is an asynchronous
//! operation. Because Rust does not support asynchronous drop yet, resource
//! types provide an explicit `close()` function. If the `close()` function is
//! not called, the resource will still be closed on drop, but the operation
//! will happen in the background. There is no guarantee as to **when** the
//! implicit close-on-drop operation happens, so it is recommended to explicitly
//! call `close()`.

#![warn(missing_docs)]
#![allow(clippy::missing_const_for_thread_local)]

#[cfg(feature = "runtime-uring")]
macro_rules! syscall {
    ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
        let res = unsafe { ::libc::$fn($($arg, )*) };
        if res == -1 {
            Err(::std::io::Error::last_os_error())
        } else {
            Ok(res)
        }
    }};
}

pub mod buf;
pub mod io;

#[cfg(feature = "runtime-uring")]
pub mod fs;
#[cfg(feature = "runtime-uring")]
pub mod net;
#[cfg(feature = "runtime-uring")]
mod runtime;

#[cfg(feature = "runtime-uring")]
pub use io::write::*;
#[cfg(feature = "runtime-uring")]
pub use runtime::{
    Runtime,
    driver::op::{InFlightOneshot, OneshotOutputTransform, UnsubmittedOneshot},
    spawn,
};

#[cfg(feature = "runtime-uring")]
use core::future::Future;

#[cfg(feature = "runtime-uring")]
use runtime::driver::op::Op;

#[cfg(feature = "runtime-uring")]
/// Starts an `io_uring` enabled Tokio runtime.
///
/// All `tokio-uring` resource types must be used from within the context of a
/// runtime. The `start` method initializes the runtime and runs it for the
/// duration of `future`.
///
/// The `tokio-uring` runtime is compatible with all Tokio, so it is possible to
/// run Tokio based libraries (e.g. hyper) from within the tokio-uring runtime.
/// A `tokio-uring` runtime consists of a Tokio `current_thread` runtime and an
/// `io-uring` driver. All tasks spawned on the `tokio-uring` runtime are
/// executed on the current thread. To add concurrency, spawn multiple threads,
/// each with a `tokio-uring` runtime.
pub fn start<F: Future>(future: F) -> F::Output {
    let rt = Runtime::new(&builder()).unwrap();
    rt.block_on(future)
}

#[cfg(feature = "runtime-uring")]
/// Creates and returns an io_uring::Builder that can then be modified
/// through its implementation methods.
pub fn uring_builder() -> io_uring::Builder {
    io_uring::IoUring::builder()
}

#[cfg(feature = "runtime-uring")]
/// Builder API that can create and start the `io_uring` runtime with non-default parameters,
/// while abstracting away the underlying io_uring crate.
pub struct Builder {
    entries: u32,
    urb: io_uring::Builder,
}

#[cfg(feature = "runtime-uring")]
/// Constructs a [`Builder`] with default settings.
pub fn builder() -> Builder {
    Builder {
        entries: 256,
        urb: io_uring::IoUring::builder(),
    }
}

#[cfg(feature = "runtime-uring")]
impl Builder {
    /// Sets the number of Submission Queue entries in uring.
    pub fn entries(&mut self, sq_entries: u32) -> &mut Self {
        self.entries = sq_entries;
        self
    }

    /// Replaces the default [`io_uring::Builder`], which controls the settings for the
    /// inner `io_uring` API.
    pub fn uring_builder(&mut self, b: &io_uring::Builder) -> &mut Self {
        self.urb = b.clone();
        self
    }

    /// Starts an `io_uring` enabled Tokio runtime.
    pub fn start<F: Future>(&self, future: F) -> F::Output {
        let rt = runtime::Runtime::new(self).unwrap();
        rt.block_on(future)
    }
}

/// A specialized `Result` type for `io-uring` operations with buffers.
pub type BufResult<T, B> = (std::io::Result<T>, B);

#[cfg(feature = "runtime-uring")]
/// The simplest possible operation. Just posts a completion event, nothing else.
pub async fn no_op() -> std::io::Result<()> {
    let op = Op::<io::NoOp>::no_op().unwrap();
    op.await
}