Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

One I/O Task, Fully Traced

Consider a spawned future awaiting readability from a TCP stream.

1. Spawn preserves the public output type

spawn<F> accepts a concrete Future + Send + 'static and returns JoinHandle<F::Output>. Internally, Tokio allocates one Cell<F, S> containing the future, scheduler handle, atomic task state, join waker, and intrusive-list links.

The scheduler cannot have a separate queue type for every F. RawTask retains only a pointer to the common header. A per-F, S vtable knows how to poll, schedule, deallocate, cancel, and read the output.

2. Initial notification enters a run queue

The task starts with NOTIFIED set and references for runtime ownership, the runnable notification, and the JoinHandle. A spawn from a worker normally uses local scheduling; a spawn from elsewhere enters the global injection queue and unparks a worker.

3. A worker polls the future

The worker selects local work, periodically checks global work, and otherwise steals from peers. Before polling, the task state atomically moves from idle to RUNNING, preventing concurrent poll or cancellation-drop access.

The future calls the TCP read implementation. No bytes are ready, so the resource’s ScheduledIo stores the task waker for readable interest and the future returns Poll::Pending. The task transitions back to idle.

4. The worker does something else

It polls another runnable task. If no work remains, a worker parks through the combined time/I/O driver instead of spinning.

5. Mio reports readiness

The OS poller returns an event. Tokio converts it to its readiness flags, updates the ScheduledIo atomic state, removes matching stored wakers under a lock, releases the lock, and wakes them.

6. Wake means schedule, not execute immediately

The raw waker atomically sets NOTIFIED. If the task was idle and not already queued, its scheduler submits one runnable reference and unparks a worker. Repeated wake-ups coalesce while NOTIFIED is already set.

7. The next poll makes progress

A worker polls the future again. The read syscall now succeeds. If the outer future completes, its output replaces the future in the task stage, state moves to COMPLETE, and the waker registered by JoinHandle is invoked.

The awaiting caller later takes F::Output through the typed join handle. The scheduler queue never needed to know that output type.