Skip to content

Commit

Permalink
driver: add Builder API as an option to start (#113)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
FrankReh authored Sep 19, 2022
1 parent 4026539 commit 20da163
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 6 deletions.
4 changes: 2 additions & 2 deletions src/driver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ struct Ops(Slab<op::Lifecycle>);
scoped_thread_local!(pub(crate) static CURRENT: Rc<RefCell<Inner>>);

impl Driver {
pub(crate) fn new() -> io::Result<Driver> {
let uring = IoUring::new(256)?;
pub(crate) fn new(b: &crate::Builder) -> io::Result<Driver> {
let uring = b.urb.build(b.entries)?;

let inner = Rc::new(RefCell::new(Inner {
ops: Ops::new(),
Expand Down
2 changes: 1 addition & 1 deletion src/driver/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ mod test {
fn init() -> (Op<Rc<()>>, 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(());

Expand Down
88 changes: 87 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,96 @@ use std::future::Future;
/// }
/// ```
pub fn start<F: Future>(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<dyn std::error::Error>> {
/// 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<F: Future>(&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
Expand Down
4 changes: 2 additions & 2 deletions src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub fn spawn<T: std::future::Future + 'static>(task: T) -> tokio::task::JoinHand
}

impl Runtime {
pub(crate) fn new() -> io::Result<Runtime> {
pub(crate) fn new(b: &crate::Builder) -> io::Result<Runtime> {
let rt = tokio::runtime::Builder::new_current_thread()
.on_thread_park(|| {
CURRENT.with(|x| {
Expand All @@ -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 })
Expand Down

0 comments on commit 20da163

Please sign in to comment.