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

Cancellation, Panics, Shutdown, and Concurrency Safety

Cancellation is a state transition

JoinHandle::abort sets the task’s cancelled state and schedules it if needed. The next exclusive task-harness transition drops the future. Local variables run their destructors, but code after the suspended .await does not run.

Cancellation is therefore not rollback. Application invariants spanning multiple awaits need guards, transactions, idempotence, or an explicit shutdown protocol.

Panic and cancellation are join errors

The task harness catches a panic at the task boundary and stores a JoinError; cancellation produces another JoinError classification. The worker thread can continue running unrelated tasks. Business errors remain the future’s own Result inside the successful join output.

Dropping a join handle detaches

JoinHandle represents interest in the output, not ownership of execution. Dropping it clears join interest and lets the task continue. Explicit abort is the cancellation operation.

Blocking work has weaker cancellation

Once a spawn_blocking closure starts, Tokio cannot abort arbitrary synchronous code. Runtime shutdown may wait indefinitely unless the caller uses a timeout shutdown method, and the closure itself needs a cooperative stop mechanism for bounded termination.

Runtime shutdown closes admission before draining storage

The multi-thread scheduler closes both its owned-task collection and global injection queue, wakes workers, cancels owned tasks, then empties local and global runnable queues. Both structures need closed state to prevent a spawn race from leaving an unreachable reference cycle.

The I/O driver marks registrations shut down and wakes their waiters. The time driver advances outstanding timers into terminal error/wake behavior. Resource futures do not remain silently pending after their driver disappears.

Low-level races are tested systematically

Tokio has dedicated Loom tests for task-state combinations, queues, shutdown, oneshot channels, and schedulers. Loom explores legal thread interleavings against modeled atomics and locks. Ordinary unit tests rarely encounter the one ordering that breaks a custom waker or refcount protocol.

The production lesson is not “use more atomics.” It is that once correctness depends on a compact atomic state machine, transitions need written invariants and systematic interleaving tests.