#io-uring #async-io #linux #io #non-blocking #linux-kernel #async

uringy

A simple single-threaded concurrency runtime for Rust based on io_uring

2 unstable releases

0.5.0 Aug 30, 2023
0.4.0 Jul 26, 2022
0.3.0 Apr 29, 2022
0.2.0 Apr 29, 2022
0.1.2 Nov 16, 2021

#1695 in Asynchronous

24 downloads per month

MIT license

57KB
1K SLoC

Uringy

website github crates-io docs-rs license

Writing concurrent code in Rust doesn't need to be painful. Uringy is a runtime that combines structured concurrency, a single-threaded design, and Linux's io_uring. Intended for server applications, from simple single-threaded to highly scalable thread-per-core designs.

Goals

Simple API

  • Familiar blocking syntax which closely mirrors Rust's standard library
  • Avoid async/await's limitations and footguns
  • Easy to learn with stellar documentation and examples
  • Spawn with non-Send and non-'static types
  • Leak-free hierarchy of fibers with first-class cancellation support

Performant

  • Issue non-blocking, batched, zero-copy syscalls with io_uring
  • Efficient context switching with cooperative multitasking
  • Atomic-free scheduler, parallelized manually if required

Quick to compile

  • Compile only what you need using [cargo features](#Compile Time Flags)
  • Minimal dependencies
  • Minimal use of macros

Quick Start

Install Rust and create a new cargo project.

Add uringy as a dependency: cargo add uringy

Then replace src/main.rs with:

// No need for async main
#[uringy::start]
fn main() {
    let handle = uringy::fiber::spawn(|| tcp_echo_server(9000)); // No need for async block

    uringy::signals().filter(Signal::is_terminal).next().unwrap();
    uringy::println!("gracefully shutting down");
    handle.cancel(); // Cancellation propagates throughout the entire fiber hierarchy

    // Automatically waits for all fibers to complete
}

// No need for async functions
fn tcp_echo_server(port: u16) {
    let listener = uringy::net::TcpListener::bind(("0.0.0.0", port)).unwrap();
    uringy::println!("listening for TCP connections on port {port}"); // No need for .await
    let mut connections = listener.incoming();
    while let Ok((stream, _)) = connections.next() {
        uringy::fiber::spawn(move || handle_connection(stream));
    }
}

fn handle_connection(tcp: TcpStream) {
    let (mut r, mut w) = stream.split();
    let _ = std::io::copy(&mut r, &mut w); // TcpStream implements std::io's Read and Write
}

And run your project using: cargo run --release

If you're using macOS, use a Linux virtual machine or a docker container. If you're using Windows, use WSL.

For more, check out the examples directory.

Compile Time Flags

There are currently no cargo flags.

Comparison with Other Runtimes

std thread uringy fiber tokio task
OS support all Linux most
IO interface blocking io_uring epoll + thread pool
function color sync sync sync and async
start N/A 27 μs 27.5 μs (3.5 μs using current thread scheduler)
spawn 9828 ns 59 ns 907 ns (58ns using current thread scheduler)
spawn Send bound yes no yes, unless using LocalSet
spawn 'static bound yes, unless using scope yes, unless using scope yes
stack size virtual 8MB (configurable), 4KB increments virtual 128KB (configurable), 4KB increments perfectly sized
stack limitations may overflow may overflow can't use recursion
context switch 1405 ns 60 ns 1328 ns (308 ns using current thread scheduler)
multi-tasking preemptive cooperative mostly cooperative
structured concurrency no guarantees parent fiber outlives its children no guarantees
runs until main thread completes all fibers complete block_on completes
parallelism automatic manual automatic, unless using current thread scheduler
userspace scheduler N/A minimal work stealing
cancellation using esoteric unix signals first class, voluntary leaks memory, causes bugs

Supported Rust Versions

The MSRV is 1.70.0 (released in June 2023). Check your Rust version by running rustc --version in a terminal.

Supported Linux Kernel Versions

The minimum kernel version is 6.1 (released in December 2022). Check your kernel version by running uname -r in a terminal.

License

Uringy is licensed under the MIT license. It's a permissive license, which basically means you can do whatever you want.

Dependencies

~475–660KB
~16K SLoC