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