## 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`, and `common` utilities) rather than a full async runtime.
- **Runtime-agnostic**: Works with standard `futures` and 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 `Timer` abstraction for scheduling waits with millisecond-level control.
- Designed to handle both single-shot and many concurrent timers efficiently.
- **Networking (`async_foundation::net`)**
- `TcpReadStream` / `TcpWriteStream` thin async wrappers around non-async mio types, adding Future/AsyncRead/AsyncWrite behavior with minimal overhead compared to using mio directly.
- `UdpSocket` async wrapper over standard UDP sockets.
- **Common utilities (`async_foundation::common`)**
- `ReadyFuture` and related state/observable types for composing async workflows.
For a full API overview, see the crate docs once published on docs.rs.
### Examples
#### Timer
```rust
use async_foundation::timer::timer::Timer;
use futures::executor::block_on;
use std::time::Duration;
let mut timer = Timer::new();
block_on(async {
// Wait for 100ms
timer.wait(Duration::from_millis(100)).await;
// Multiple concurrent timers
let t1 = timer.wait(Duration::from_millis(50));
let t2 = timer.wait(Duration::from_millis(100));
futures::join!(t1, t2);
});
```
#### TCP Networking
```rust
use async_foundation::net::tcp_stream::TcpStream;
use futures::executor::block_on;
use futures::{AsyncReadExt, AsyncWriteExt};
use std::net::TcpListener;
use std::thread;
// Server side
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
// Handle connection
}
});
// Client side
block_on(async {
let mut stream = TcpStream::connect(addr).unwrap();
// Write data
stream.write_all(b"Hello, world!").await.unwrap();
// Read data
let mut buf = [0u8; 1024];
let n = stream.read(&mut buf).await.unwrap();
});
```
#### UDP Networking
```rust
use async_foundation::net::udp_socket::UdpSocket;
use futures::executor::block_on;
use std::net::SocketAddr;
block_on(async {
// Bind a UDP socket (non-blocking)
let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
// Send data (non-blocking, can be used in async context)
let target: SocketAddr = "127.0.0.1:8080".parse().unwrap();
socket.send_to(b"Hello", target).unwrap();
// Receive data (non-blocking)
let mut buf = [0u8; 1024];
let (n, from) = socket.recv_from(&mut buf).unwrap();
// UdpSocket also implements AsyncRead/AsyncWrite for stream-like usage
});
```
#### ReadyFuture
```rust
use async_foundation::common::ready_future::ReadyFuture;
use futures::executor::block_on;
// Create a future that can be completed later
let future = ReadyFuture::new();
let future_clone = future.clone();
// Complete it from another context
future.complete(42);
// Await the result
let result = block_on(future_clone);
match result {
async_foundation::common::ready_future_state::ReadyFutureResult::Completed(value) => {
assert_eq!(value, 42);
}
_ => unreachable!(),
}
// Or create a pre-completed future
let completed = ReadyFuture::new_completed("done");
let result = block_on(completed);
```
#### ReadyObservable
```rust
use async_foundation::common::ready_observable::ReadyObservable;
use futures::executor::block_on;
let mut observable = ReadyObservable::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(async {
futures::join!(wait1, wait2);
});
```
### Performance characteristics
This crate is designed for **low overhead and predictable performance**:
- **Timers**: More efficient than `std::thread::sleep` for async contexts, with lower overhead than heavyweight runtime timers. Optimized for handling many concurrent timers efficiently.
- **Networking**: Thin wrappers around `mio` with minimal overhead compared to direct `mio` usage, 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::sleep` or 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_state` and `timer_future_state`).
- It is easier to embed in minimal or experimental executors where you don’t want to pull in a full runtime.
- For **networking**, the focus is on:
- Providing **thin, benchmarked wrappers** around `mio` and standard sockets.
- Making it straightforward to measure and reason about the overhead of stream/socket wrappers and timeout handling.
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.