From 20da163283dda1767fbc84f3ef632ef4d3222f27 Mon Sep 17 00:00:00 2001 From: FrankReh Date: Mon, 19 Sep 2022 10:53:58 -0400 Subject: [PATCH] driver: add Builder API as an option to start (#113) Add a Builder API as an option to start the tokio_uring runtime. The preexisting tokio_uring::start function is maintained. A new start mechanism is introduced that allows specifying the parts that the runtime start and the driver initialize use. An example of the new usage is provided in the Builder::start method. A goal of this Builder was to remain independant of the io-uring Builder progress and allow the caller to build their io-uring options and pass them to this crate's Builder. As new kernel versions support more io_uring setup parameters, the tokio io-uring crate will add more Builder options and they should not require lock-step changes in this crate. Fixes #105 --- src/driver/mod.rs | 4 +-- src/driver/op.rs | 2 +- src/lib.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++- src/runtime.rs | 4 +-- 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/driver/mod.rs b/src/driver/mod.rs index 54895f4e..c2813d96 100644 --- a/src/driver/mod.rs +++ b/src/driver/mod.rs @@ -65,8 +65,8 @@ struct Ops(Slab); scoped_thread_local!(pub(crate) static CURRENT: Rc>); impl Driver { - pub(crate) fn new() -> io::Result { - let uring = IoUring::new(256)?; + pub(crate) fn new(b: &crate::Builder) -> io::Result { + let uring = b.urb.build(b.entries)?; let inner = Rc::new(RefCell::new(Inner { ops: Ops::new(), diff --git a/src/driver/op.rs b/src/driver/op.rs index d244080e..22f6de90 100644 --- a/src/driver/op.rs +++ b/src/driver/op.rs @@ -308,7 +308,7 @@ mod test { fn init() -> (Op>, crate::driver::Driver, Rc<()>) { use crate::driver::Driver; - let driver = Driver::new().unwrap(); + let driver = Driver::new(&crate::builder()).unwrap(); let handle = driver.inner.clone(); let data = Rc::new(()); diff --git a/src/lib.rs b/src/lib.rs index 74962405..f1ffdb90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,10 +140,96 @@ use std::future::Future; /// } /// ``` pub fn start(future: F) -> F::Output { - let mut rt = runtime::Runtime::new().unwrap(); + let mut rt = runtime::Runtime::new(&builder()).unwrap(); rt.block_on(future) } +/// Create and return an io_uring::Builder that can then be modified +/// through its implementation methods. +/// +/// This function is provided to avoid requiring the user of this crate from +/// having to use the io_uring crate as well. Refer to Builder::start example +/// for its intended usage. +pub fn uring_builder() -> io_uring::Builder { + io_uring::IoUring::builder() +} + +/// Builder API to allow starting the runtime and creating the io_uring driver with non-default +/// parameters. +// #[derive(Clone, Default)] +pub struct Builder { + entries: u32, + urb: io_uring::Builder, +} + +/// Return a Builder to allow setting parameters before calling the start method. +/// Returns a Builder with our default values, all of which can be replaced with the methods below. +/// +/// Refer to Builder::start for an example. +pub fn builder() -> Builder { + Builder { + entries: 256, + urb: io_uring::IoUring::builder(), + } +} + +impl Builder { + /// Set number of submission queue entries in uring. + /// + /// The kernel will ensure it uses a power of two and will round this up if necessary. + /// The kernel requires the number of completion queue entries to be larger than + /// the submission queue entries so generally will double the sq entries count. + /// + /// The caller can specify even a larger cq entries count by using the uring_builder + /// as shown in the start example below. + pub fn entries(&mut self, e: u32) -> &mut Self { + self.entries = e; + self + } + + /// Replace the default io_uring Builder. This allows the caller to craft the io_uring Builder + /// using the io_uring crate's Builder API. + /// + /// Refer to the Builder start method for an example. + /// Refer to the io_uring::builder documentation for all the supported methods. + pub fn uring_builder(&mut self, b: &io_uring::Builder) -> &mut Self { + self.urb = b.clone(); + self + } + + /// Start an `io_uring` enabled Tokio runtime. + /// + /// # Examples + /// + /// Creating a uring driver with only 64 submission queue entries but + /// many more completion queue entries. + /// + /// ```no_run + /// use tokio::net::TcpListener; + /// + /// fn main() -> Result<(), Box> { + /// tokio_uring::builder() + /// .entries(64) + /// .uring_builder(tokio_uring::uring_builder() + /// .setup_cqsize(1024) + /// ) + /// .start(async { + /// let listener = TcpListener::bind("127.0.0.1:8080").await?; + /// + /// loop { + /// let (socket, _) = listener.accept().await?; + /// // process socket + /// } + /// } + /// ) + /// } + /// ``` + pub fn start(&self, future: F) -> F::Output { + let mut rt = runtime::Runtime::new(self).unwrap(); + rt.block_on(future) + } +} + /// A specialized `Result` type for `io-uring` operations with buffers. /// /// This type is used as a return value for asynchronous `io-uring` methods that diff --git a/src/runtime.rs b/src/runtime.rs index c0b69e75..6f851ccd 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -48,7 +48,7 @@ pub fn spawn(task: T) -> tokio::task::JoinHand } impl Runtime { - pub(crate) fn new() -> io::Result { + pub(crate) fn new(b: &crate::Builder) -> io::Result { let rt = tokio::runtime::Builder::new_current_thread() .on_thread_park(|| { CURRENT.with(|x| { @@ -62,7 +62,7 @@ impl Runtime { let driver = { let _guard = rt.enter(); - AsyncFd::new(Driver::new()?)? + AsyncFd::new(Driver::new(b)?)? }; Ok(Runtime { driver, local, rt })