nexus-async-rt
Single-threaded async runtime for latency-sensitive systems. Built on mio.
Not a tokio replacement — a purpose-built alternative for single-threaded event loops where predictable latency matters more than multi-threaded throughput.
When to use this vs tokio
Use nexus-async-rt when:
- Single-threaded event loop (one core, no work-stealing)
- Predictable tail latency (no scheduler jitter)
- Zero-alloc task spawning on the hot path (slab allocation)
- Futures are
!Send— and that's fine - You're building on the nexus ecosystem (nexus-net, nexus-rt)
Use tokio when:
- Multi-threaded execution or work-stealing
- tokio ecosystem (tower, hyper, tonic, etc.)
- Futures need to be
Sendacross threads - Broad community support matters
Both can coexist in the same process. Use nexus-async-rt for the latency-critical event loop and tokio for everything else.
Quick start
use *;
use WorldBuilder;
let mut world = new.build;
let mut rt = new;
rt.block_on;
What you get
Task spawning
Two strategies, same API:
// Box-allocated — default, no setup needed
let handle = spawn_boxed;
// Slab-allocated — pre-allocated, zero-alloc hot path
let handle = spawn_slab;
Both return JoinHandle<T> — await for the result, drop to detach,
or call abort() to cancel (consumes the handle).
Slab allocation (zero-alloc spawn)
For hot-path tasks where allocation jitter is unacceptable:
// SAFETY: single-threaded runtime owns the slab.
let slab = unsafe ;
let mut rt = builder
.slab_unbounded
.build;
rt.block_on;
Or claim a slot first, spawn later:
if let Some = try_claim_slab
Timers
use Duration;
// Sleep
sleep.await;
// Timeout
let result = timeout.await;
// Interval
let mut tick = interval;
loop
I/O (mio-based)
use ;
// Client
let stream = connect?;
// Server
let listener = bind?;
let = listener.accept.await?;
Channels
Three flavors for different use cases:
use channel;
// Local MPSC — !Send, zero atomics, single-threaded
let = channel;
// Cross-thread MPSC — Sender: Clone + Send
let = channel;
// Cross-thread SPSC — fastest cross-thread path
let = channel;
World access
Access nexus-rt World resources from async tasks:
with_world;
Graceful shutdown
rt.block_on;
Cancellation
let token = new;
let child = token.child_token;
spawn_boxed;
token.cancel; // cancels all children
JoinHandle
spawn_boxed and spawn_slab return JoinHandle<T>:
- Await — get the result:
let val = handle.await; - Detach — drop the handle, task continues, output dropped on completion
- Abort —
handle.abort()consumes the handle, future dropped on next poll - Check —
handle.is_finished()for non-blocking status
JoinHandle is !Send and !Sync — stays on the executor thread.
Performance
| Path | p50 |
|---|---|
| Task dispatch (poll cycle) | 55-64 cycles |
| Local channel try_send+try_recv | 13 ns |
| MPSC channel try_send+try_recv | 22 ns |
| SPSC channel try_send+try_recv | 15 ns |
| Cross-thread channel (busy spin) | 15 ns |
| Cross-thread channel (park/epoll) | 1.7 us |
| Tokio-compat waker bridge | 76 ns |
Features
| Feature | Default | Description |
|---|---|---|
tokio-compat |
No | Adapters for bridging tokio and nexus-async-rt in the same process |
Dependencies
- mio — I/O event loop (epoll/kqueue)
- nexus-rt — World/WorldBuilder for typed resource storage
- nexus-slab — Optional pre-allocated task storage
- nexus-timer — Hierarchical timer wheel
- nexus-queue / nexus-logbuf — Lock-free internal queues
Platform support
Unix only (#![cfg(unix)]). Linux is the primary target, macOS supported.