kvarn_tokio_uring/
lib.rs

1//! Tokio-uring provides a safe [io-uring] interface for the Tokio runtime. The
2//! library requires Linux kernel 5.10 or later.
3//!
4//! [io-uring]: https://kernel.dk/io_uring.pdf
5//!
6//! # Getting started
7//!
8//! Using `tokio-uring` requires starting a [`tokio-uring`] runtime. This
9//! runtime internally manages the main Tokio runtime and a `io-uring` driver.
10//!
11//! ```no_run
12//! use tokio_uring::fs::File;
13//!
14//! fn main() -> Result<(), Box<dyn std::error::Error>> {
15//!     tokio_uring::start(async {
16//!         // Open a file
17//!         let file = File::open("hello.txt").await?;
18//!
19//!         let buf = vec![0; 4096];
20//!         // Read some data, the buffer is passed by ownership and
21//!         // submitted to the kernel. When the operation completes,
22//!         // we get the buffer back.
23//!         let (res, buf) = file.read_at(buf, 0).await;
24//!         let n = res?;
25//!
26//!         // Display the contents
27//!         println!("{:?}", &buf[..n]);
28//!
29//!         Ok(())
30//!     })
31//! }
32//! ```
33//!
34//! Under the hood, `tokio_uring::start` starts a [`current-thread`] Runtime.
35//! For concurrency, spawn multiple threads, each with a `tokio-uring` runtime.
36//! The `tokio-uring` resource types are optimized for single-threaded usage and
37//! most are `!Sync`.
38//!
39//! # Submit-based operations
40//!
41//! Unlike Tokio proper, `io-uring` is based on submission based operations.
42//! Ownership of resources are passed to the kernel, which then performs the
43//! operation. When the operation completes, ownership is passed back to the
44//! caller. Because of this difference, the `tokio-uring` APIs diverge.
45//!
46//! For example, in the above example, reading from a `File` requires passing
47//! ownership of the buffer.
48//!
49//! # Closing resources
50//!
51//! With `io-uring`, closing a resource (e.g. a file) is an asynchronous
52//! operation. Because Rust does not support asynchronous drop yet, resource
53//! types provide an explicit `close()` function. If the `close()` function is
54//! not called, the resource will still be closed on drop, but the operation
55//! will happen in the background. There is no guarantee as to **when** the
56//! implicit close-on-drop operation happens, so it is recommended to explicitly
57//! call `close()`.
58
59#![warn(missing_docs)]
60
61macro_rules! syscall {
62    ($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
63        let res = unsafe { libc::$fn($($arg, )*) };
64        if res == -1 {
65            Err(std::io::Error::last_os_error())
66        } else {
67            Ok(res)
68        }
69    }};
70}
71
72#[macro_use]
73mod future;
74mod io;
75mod runtime;
76
77pub mod buf;
78pub mod fs;
79pub mod net;
80
81pub use io::write::*;
82pub use runtime::driver::op::{InFlightOneshot, OneshotOutputTransform, UnsubmittedOneshot};
83pub use runtime::spawn;
84pub use runtime::Runtime;
85
86use crate::runtime::driver::op::Op;
87use std::future::Future;
88
89/// Starts an `io_uring` enabled Tokio runtime.
90///
91/// All `tokio-uring` resource types must be used from within the context of a
92/// runtime. The `start` method initializes the runtime and runs it for the
93/// duration of `future`.
94///
95/// The `tokio-uring` runtime is compatible with all Tokio, so it is possible to
96/// run Tokio based libraries (e.g. hyper) from within the tokio-uring runtime.
97/// A `tokio-uring` runtime consists of a Tokio `current_thread` runtime and an
98/// `io-uring` driver. All tasks spawned on the `tokio-uring` runtime are
99/// executed on the current thread. To add concurrency, spawn multiple threads,
100/// each with a `tokio-uring` runtime.
101///
102/// # Examples
103///
104/// Basic usage
105///
106/// ```no_run
107/// use tokio_uring::fs::File;
108///
109/// fn main() -> Result<(), Box<dyn std::error::Error>> {
110///     tokio_uring::start(async {
111///         // Open a file
112///         let file = File::open("hello.txt").await?;
113///
114///         let buf = vec![0; 4096];
115///         // Read some data, the buffer is passed by ownership and
116///         // submitted to the kernel. When the operation completes,
117///         // we get the buffer back.
118///         let (res, buf) = file.read_at(buf, 0).await;
119///         let n = res?;
120///
121///         // Display the contents
122///         println!("{:?}", &buf[..n]);
123///
124///         Ok(())
125///     })
126/// }
127/// ```
128///
129/// Using Tokio types from the `tokio-uring` runtime
130///
131///
132/// ```no_run
133/// use tokio::net::TcpListener;
134///
135/// fn main() -> Result<(), Box<dyn std::error::Error>> {
136///     tokio_uring::start(async {
137///         let listener = TcpListener::bind("127.0.0.1:8080").await?;
138///
139///         loop {
140///             let (socket, _) = listener.accept().await?;
141///             // process socket
142///         }
143///     })
144/// }
145/// ```
146pub fn start<F: Future>(future: F) -> F::Output {
147    let rt = runtime::Runtime::new(&builder()).unwrap();
148    rt.block_on(future)
149}
150
151/// Creates and returns an io_uring::Builder that can then be modified
152/// through its implementation methods.
153///
154/// This function is provided to avoid requiring the user of this crate from
155/// having to use the io_uring crate as well. Refer to Builder::start example
156/// for its intended usage.
157pub fn uring_builder() -> io_uring::Builder {
158    io_uring::IoUring::builder()
159}
160
161/// Builder API that can create and start the `io_uring` runtime with non-default parameters,
162/// while abstracting away the underlying io_uring crate.
163// #[derive(Clone, Default)]
164pub struct Builder {
165    entries: u32,
166    urb: io_uring::Builder,
167}
168
169/// Constructs a [`Builder`] with default settings.
170///
171/// Use this to alter submission and completion queue parameters, and to create the io_uring
172/// Runtime.
173///
174/// Refer to [`Builder::start`] for an example.
175pub fn builder() -> Builder {
176    Builder {
177        entries: 256,
178        urb: io_uring::IoUring::builder(),
179    }
180}
181
182impl Builder {
183    /// Sets the number of Submission Queue entries in uring.
184    ///
185    /// The default value is 256.
186    /// The kernel requires the number of submission queue entries to be a power of two,
187    /// and that it be less than the number of completion queue entries.
188    /// This function will adjust the `cq_entries` value to be at least 2 times `sq_entries`
189    pub fn entries(&mut self, sq_entries: u32) -> &mut Self {
190        self.entries = sq_entries;
191        self
192    }
193
194    /// Replaces the default [`io_uring::Builder`], which controls the settings for the
195    /// inner `io_uring` API.
196    ///
197    /// Refer to the [`io_uring::Builder`] documentation for all the supported methods.
198    pub fn uring_builder(&mut self, b: &io_uring::Builder) -> &mut Self {
199        self.urb = b.clone();
200        self
201    }
202
203    /// Starts an `io_uring` enabled Tokio runtime.
204    ///
205    /// # Examples
206    ///
207    /// Creating a uring driver with only 64 submission queue entries but
208    /// many more completion queue entries.
209    ///
210    /// ```no_run
211    /// use tokio::net::TcpListener;
212    ///
213    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
214    ///     tokio_uring::builder()
215    ///         .entries(64)
216    ///         .uring_builder(tokio_uring::uring_builder()
217    ///             .setup_cqsize(1024)
218    ///             )
219    ///         .start(async {
220    ///             let listener = TcpListener::bind("127.0.0.1:8080").await?;
221    ///
222    ///             loop {
223    ///                 let (socket, _) = listener.accept().await?;
224    ///                 // process socket
225    ///             }
226    ///         }
227    ///     )
228    /// }
229    /// ```
230    pub fn start<F: Future>(&self, future: F) -> F::Output {
231        let rt = runtime::Runtime::new(self).unwrap();
232        rt.block_on(future)
233    }
234}
235
236/// A specialized `Result` type for `io-uring` operations with buffers.
237///
238/// This type is used as a return value for asynchronous `io-uring` methods that
239/// require passing ownership of a buffer to the runtime. When the operation
240/// completes, the buffer is returned whether or not the operation completed
241/// successfully.
242///
243/// # Examples
244///
245/// ```no_run
246/// use tokio_uring::fs::File;
247///
248/// fn main() -> Result<(), Box<dyn std::error::Error>> {
249///     tokio_uring::start(async {
250///         // Open a file
251///         let file = File::open("hello.txt").await?;
252///
253///         let buf = vec![0; 4096];
254///         // Read some data, the buffer is passed by ownership and
255///         // submitted to the kernel. When the operation completes,
256///         // we get the buffer back.
257///         let (res, buf) = file.read_at(buf, 0).await;
258///         let n = res?;
259///
260///         // Display the contents
261///         println!("{:?}", &buf[..n]);
262///
263///         Ok(())
264///     })
265/// }
266/// ```
267pub type BufResult<T, B> = (std::io::Result<T>, B);
268
269/// The simplest possible operation. Just posts a completion event, nothing else.
270///
271/// This has a place in benchmarking and sanity checking uring.
272///
273/// # Examples
274///
275/// ```no_run
276/// fn main() -> Result<(), Box<dyn std::error::Error>> {
277///     tokio_uring::start(async {
278///         // Place a NoOp on the ring, and await completion event
279///         tokio_uring::no_op().await?;
280///         Ok(())
281///     })
282/// }
283/// ```
284pub async fn no_op() -> std::io::Result<()> {
285    let op = Op::<io::NoOp>::no_op().unwrap();
286    op.await
287}