Skip to content

Q072. How does the Tokio runtime schedule tasks?

Short Answer

Tokio stores spawned futures as tasks and polls ready tasks on executor workers. A task that returns Pending is not polled again until a waker marks it ready. The multi-thread runtime uses worker-local queues, a shared queue, and work stealing to distribute runnable tasks; the current-thread runtime polls all tasks on one thread. Scheduling is cooperative, so tasks must yield through pending awaits or explicit yielding.

Deep Dive

Tokio combines task scheduling with a timer and I/O driver. Readiness events wake tasks, which are queued for polling. Worker-local queues improve cache locality, while stealing helps balance uneven workloads.

Tokio also uses cooperative budgeting to stop one task from consuming unlimited immediately-ready operations without giving the scheduler a chance to run peers. This improves fairness but does not preempt arbitrary synchronous loops; such loops must yield or move off the executor.

Scheduling order is not a stable application contract. Correct code must not depend on a particular worker, task order, or immediate execution after spawn. Shared state still requires synchronization, and task-local data should be used when thread-local assumptions are invalid.

Internal Model

A task contains its pinned future, readiness state, scheduling links, and completion data. A waker atomically transitions or marks readiness and enqueues the task when necessary.

The runtime builder chooses current-thread or multi-thread scheduling and configures worker counts, blocking limits, drivers, and other runtime behavior.

Example

rust
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
    let first = tokio::spawn(async {
        tokio::task::yield_now().await;
        20_u64
    });
    let second = tokio::spawn(async { 22_u64 });

    assert_eq!(first.await.unwrap() + second.await.unwrap(), 42);
}

Common Mistakes

  • Assuming Tokio preempts long synchronous task code.
  • Depending on a spawned task running immediately or on one specific worker.
  • Treating work stealing as a guarantee of equal per-task latency.

Follow-ups

  • What problem does cooperative budgeting solve?
  • How does the current-thread runtime differ operationally?
  • What happens between an I/O readiness event and the next task poll?

References

  • Tokio Documentation
  • Async Book