On this page
tokio Runtime
tokio’s reactor (based on epoll/kqueue/IOCP) handles async I/O, and the executor schedules Futures across multiple threads using work-stealing—the combination forms the de facto standard runtime for Rust async.
spawnsubmits Futures to a thread pool,select!waits on multiple async operations simultaneously—understanding the division of labor between reactor and executor reveals tokio’s performance model.
tokio’s Three-Layer Architecture
flowchart TD
TASK["🧵 Your async task<br/>tokio::spawn"]
TASK --> QUEUE["📋 Task Queue<br/>Futures waiting to be scheduled"]
QUEUE --> EXECUTOR["⚙️ Executor<br/>work-stealing scheduler<br/>one worker thread per CPU core"]
EXECUTOR --> REACTOR["🔌 Reactor<br/>epoll (Linux) / kqueue (macOS)<br/>IOCP (Windows)"]
REACTOR --> KERNEL["🐧 OS Kernel"]
REACTOR -.->|"I/O ready → wake task"| QUEUE
classDef user fill:#e3f2fd,stroke:#1565c0
classDef middle fill:#fff3e0,stroke:#ef6c00
classDef os fill:#f3e5f5,stroke:#7b1fa2
class TASK,QUEUE user
class EXECUTOR,REACTOR middle
class KERNEL os
Reactor: Listens to all registered I/O sources (sockets, timers, signals) → when I/O is ready → notifies the corresponding task to wake up.
Executor: Maintains the task queue → woken tasks regain CPU time.
Your code: Simply calls async fn and uses .await, without directly interacting with the reactor or executor.
Two Types of Runtimes
// Multi-threaded (default)
async
// Equivalent to:
// Single-threaded (for simple scenarios, e.g., no need for cross-thread synchronization)
async
spawn: Independent Concurrent Tasks
let handle = spawn;
// handle: JoinHandle — you can .await it to get the return value
let result = handle.await.unwrap;
Tasks spawned via spawn can execute on different worker threads—they are truly concurrent, not just coroutines. The returned JoinHandle is itself a Future; .awaiting it waits for the task to complete.
Work-Stealing Scheduler
Each worker thread has its own local task queue. When a worker becomes idle, it randomly steals tasks from other workers. This model is more efficient than “one global queue + all workers competing”—it reduces lock contention because local push/pop operations are lock-free.
select!: Waiting on Multiple Futures
select!
Once any branch completes → the other branches are cancelled (dropped). This is async Rust’s built-in cancellation model—no explicit cancellation token is needed. The Drop implementation of the cancelled Future is responsible for cleaning up resources.
References
- tokio: docs.rs/tokio, tokio.rs
- async book: rust-lang.github.io/async-book
Keywords: tokio, reactor, executor, work stealing, spawn, select!, runtime, current_thread