Why Is It Designed This Way?
Why poll futures instead of giving each operation a thread?
Most network tasks spend most of their lifetime waiting. Storing suspended state in a future and polling only on readiness lets a small worker set manage many connections without an OS stack and scheduler entry per connection.
Why must a future return Pending with a Waker?
Pending alone would force the executor to poll every dormant task repeatedly.
The waker gives the awaited resource a direct route to make exactly that task
runnable again.
Why are tasks cooperatively scheduled?
Rust futures are ordinary state machines polled as function calls. Tokio cannot preempt arbitrary Rust code safely in the middle of a poll. Cooperative budgets and well-behaved async primitives create yield points at controlled boundaries.
Why use local queues, a LIFO slot, and a global injection queue?
Locally spawned follow-up work often shares hot data and should run quickly. Remote spawns need a synchronized entry point. Periodic global checks and a cap on repeated LIFO polls keep those locality optimizations from becoming starvation.
Why steal work?
Task arrival and wake-ups are uneven. A worker that exhausts its own queue can move runnable tasks from a busy worker, turning a fixed thread pool into useful parallelism without centralizing every pop behind one lock.
Why does I/O readiness wake tasks rather than perform their operations?
The OS reports that an operation may now succeed; it does not own the future’s
buffer, error handling, or state machine. Waking re-enters the normal poll path,
where the operation is retried and WouldBlock can clear stale readiness.
Why have a separate blocking pool?
A blocking syscall or CPU-heavy synchronous function can monopolize a runtime worker and delay every task assigned there. A separate pool contains that behavior. It is a bridge, not automatic CPU backpressure; callers may still need a semaphore or Rayon.
Why type-erase tasks internally?
spawn accepts every concrete future type, but one scheduler queue must hold
them together. A small raw pointer plus type-specific vtable retains efficient
generic construction and typed JoinHandle<T> while giving the queue one
uniform runnable representation.
Why does cancellation happen at poll boundaries?
Tokio can atomically request cancellation and safely drop a future when the task harness next owns exclusive access. It cannot forcibly interrupt arbitrary user code between Rust instructions.
Why offer current-thread, local, and multi-thread runtimes?
Concurrency does not always require parallelism, and !Send futures must stay
on one thread. Different schedulers make those constraints explicit instead of
paying for or promising movement every application does not need.