async-foundation
Foundational async primitives for Rust – lightweight timers, networking utilities, and common async building blocks designed for low overhead and predictable performance.
What this crate is
- Focused building blocks: Provides a small, well-defined set of primitives (
timer,net, andcommonutilities) rather than a full async runtime. - Runtime-agnostic: Works with standard
futuresand can be integrated into custom executors or existing ecosystems. - Performance-oriented: Comes with a comprehensive Criterion benchmark suite for timers, networking, and internal data structures.
Core components
- Timers (
async_foundation::timer)- High-level
Timerabstraction for scheduling waits with millisecond-level control. - Designed to handle both single-shot and many concurrent timers efficiently.
- High-level
- Networking (
async_foundation::net)TcpReadStream/TcpWriteStreamthin async wrappers around non-async mio types, adding Future/AsyncRead/AsyncWrite behavior with minimal overhead compared to using mio directly.UdpSocketasync wrapper over standard UDP sockets.
- Common utilities (
async_foundation::common)ReadyFutureand related state/observable types for composing async workflows.
For a full API overview, see the crate docs once published on docs.rs.
Examples
Timer
use Timer;
use block_on;
use Duration;
let mut timer = new;
block_on;
TCP Networking
use TcpStream;
use block_on;
use ;
use TcpListener;
use thread;
// Server side
let listener = bind.unwrap;
let addr = listener.local_addr.unwrap;
spawn;
// Client side
block_on;
UDP Networking
use UdpSocket;
use block_on;
use SocketAddr;
block_on;
ReadyFuture
use ReadyFuture;
use block_on;
// Create a future that can be completed later
let future = new;
let future_clone = future.clone;
// Complete it from another context
future.complete;
// Await the result
let result = block_on;
match result
// Or create a pre-completed future
let completed = new_completed;
let result = block_on;
ReadyObservable
use ReadyObservable;
use block_on;
let mut observable = default;
// Multiple tasks can wait for the observable to become ready
let wait1 = observable.wait;
let wait2 = observable.wait;
// Complete the observable (wakes all waiters)
observable.complete;
// Both futures will now resolve
block_on;
Performance characteristics
This crate is designed for low overhead and predictable performance:
- Timers: More efficient than
std::thread::sleepfor async contexts, with lower overhead than heavyweight runtime timers. Optimized for handling many concurrent timers efficiently. - Networking: Thin wrappers around
miowith minimal overhead compared to directmiousage, while providing a more ergonomic async API. - Memory: Low allocation overhead for timers and networking primitives, making it suitable for high-throughput scenarios.
How this compares to other solutions
This crate does not aim to replace full-featured async runtimes like Tokio, async-std, or smol. Instead, it targets a specific niche:
- You want lower-level control over timers and I/O than high-level runtimes typically expose.
- You want small, composable primitives that can be wired into your own executor or integrated into existing systems.
- You care about predictable overhead and want benchmarks baked into the project so you can reason about performance over time.
In practice:
- Compared to using
std::thread::sleepor ad-hoc timer wheels,async-foundation:- Provides low-overhead timer creation with explicit, benchmarked behavior for concurrent and sequential timers.
- Compared to using only high-level runtime timers (e.g.
tokio::time::sleep):- This crate gives you more visibility into timer internals (via
timer_stateandtimer_future_state). - It is easier to embed in minimal or experimental executors where you don’t want to pull in a full runtime.
- This crate gives you more visibility into timer internals (via
- For networking, the focus is on:
- Providing thin, benchmarked wrappers around
mioand standard sockets. - Making it straightforward to measure and reason about the overhead of stream/socket wrappers and timeout handling.
- Providing thin, benchmarked wrappers around
Use async-foundation when you:
- Are building your own async runtime or executor.
- Need fine-grained control over timers and timeouts.
- Want a small, well-benchmarked layer for async networking primitives without a heavyweight dependency tree.
Use a full runtime (Tokio, async-std, etc.) when you:
- Need batteries-included features (spawning, timers, I/O, synchronization, etc.) in a single crate.
- Prefer convenience over having to stitch together lower-level components yourself.
License
This project is licensed under the MIT License. See LICENSE for details.