Chapter 1 of 17

Setup

Add Nuclei, run a first program, and see which kernel interface it picked.

What you need

Rust 1.99 or later. Nuclei is written in the 2024 edition; your crate may use any edition. Each system runs on its own kernel interface:

SystemBackend
Linux, Androidio_uring where the kernel (6.1 or later) and the sandbox allow it, else epoll
illumosepoll
macOS, iOS, FreeBSD, NetBSD, OpenBSD, DragonFlykqueue
WindowsI/O completion ports

Add it

cargo add nuclei

or in Cargo.toml:

Cargo.toml
[dependencies]
nuclei = "0.5"

Nothing else is needed: the read and write helpers, the stream traits and the macros all come with the crate.

A first program

src/main.rs
#[nuclei::main]
async fn main() {
    let answer = nuclei::spawn(async { 6 * 7 }).await;
    println!("the answer is {answer}");
}
output
the answer is 42

#[nuclei::main] runs the async main on the runtime, and nuclei::spawn places a task on one of its shards; awaiting the task gives its output. There is nothing to configure: the first spawn or block_on starts the default runtime, with one worker per core the process may run on.

Features

FeatureDefaultWhat it gives
epollonNames epoll beside iouring. Epoll is built on Linux either way.
iouringonThe io_uring driver, on Linux and Android. Without it those systems run on epoll.
attributeson#[nuclei::main], #[nuclei::test] and #[nuclei::bench].
recycleoffNode memory of the lock-free queues, maps and channels underneath, from a per-thread cache instead of the global allocator.
io-statsoffEach worker's IO in stats: counts of its operations and bytes, and IO latencies when recorded (Production). Off by default; without it no IO path carries counting or timing code.

recycle is for applications: cargo turns a feature on for every user of a crate in a build, so a library that enabled it would enable it for all of them.

Cargo.toml
[dependencies]
# Linux on epoll only, keeping the attribute macros:
nuclei = { version = "0.5", default-features = false, features = ["attributes"] }

Which backend runs

src/main.rs
fn main() {
    nuclei::block_on(async {
        println!("IO runs on {:?}", nuclei::Proactor::backend());
    });
}

With nothing set, Nuclei takes io_uring where the kernel lets the process have the rings its shards use, and the platform's poller otherwise. The probe is safe, even under a seccomp filter that kills a process for calling io_uring_setup. NUCLEI_BACKEND picks one by hand: auto, io_uring or epoll on Linux, kqueue on macOS and the BSDs, iocp on Windows.

NUCLEI_BACKEND=epoll cargo run

A backend named by hand that the process cannot have is an error with the reason, never a silent fallback. Backends has the details, Errors the messages.