Inside Rust's Async Runtime: From Futures to Epoll
A systems-level exploration of how Tokio manages millions of concurrent connections with zero-cost abstractions.
Kai Nakamura
Systems Engineer
February 28, 2026
18 min read
The Async Abstraction Stack
Rust's async story is built on layers of abstraction, each adding capability while maintaining zero-cost principles.
Futures: The Foundation
At the lowest level, a Future in Rust is a state machine:
enum MyFuture {
WaitingForData { socket: TcpStream },
ProcessingData { buffer: Vec<u8> },
Done(Result<Response, Error>),
}
impl Future for MyFuture {
type Output = Result<Response, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.get_mut() {
Self::WaitingForData { socket } => {
match socket.try_read(&mut buf) {
Ok(n) => {
*self = Self::ProcessingData { buffer: buf[..n].to_vec() };
cx.waker().wake_by_ref();
Poll::Pending
}
Err(ref e) if e.kind() == WouldBlock => Poll::Pending,
Err(e) => Poll::Ready(Err(e.into())),
}
}
// ... state transitions
}
}
}
The Reactor: epoll Under the Hood
Tokio's reactor wraps the OS event notification system. On Linux, this is epoll; on macOS, kqueue. The reactor thread blocks on these syscalls, waking tasks only when their I/O is ready.
This architecture enables handling millions of connections on a single machine with predictable, low-latency performance — the backbone of modern cloud infrastructure.