Under The Hood
backend go Go 1.21+ (context package)

context.Context: Done() Channel, Parent-Child Tree Cancellation

Last updated
Prerequisites:
Go goroutines and channels
Understanding of cancellation and timeouts
  • go
  • context
  • cancellation
  • deadline
  • propagation
  • goroutine

Read at your depth

The practical view

context.Context carries cancellation, deadlines, and request-scoped values across API boundaries. Create with context.Background() (root), context.WithCancel(parent), context.WithTimeout(parent, d), context.WithDeadline(parent, t), context.WithValue(parent, key, val). Pass ctx as the FIRST parameter to functions that do work. Check cancellation: select { case <-ctx.Done(): ... } or ctx.Err(). Call the cancel function (defer cancel()) to release resources. Standard library APIs (http.Request.Context(), database/sql, net) honor context cancellation automatically.

The same idea in other frameworks

react equivalent
N/A — Go runtime/library concern.

Legacy vs modern

Manual goroutine stop flag vs context cancellation

A hand-rolled stop channel only handles one level and leaks on forget; context cascades cancellation through the whole tree and integrates with the standard library.

before → after
Manual stop channel
stop := make(chan struct{}) // ❌ OLD: one-off signal, no hierarchy
Context cancellation tree
ctx, cancel := context.WithCancel(context.Background()) // ✅ NEW
defer cancel()

Interview gotchas

Context

This tests the tree model — interviewers want the parent→child propagation and the resource-leak consequence of forgetting cancel(), which is a common production bug.

The mechanical answer

Derived contexts (WithCancel/WithTimeout/WithDeadline) are child nodes: each registers itself in its parent's children map. When cancel() is called on a context, it closes that context's done channel (once, guarded by a mutex), sets the err, and recursively cancels every registered child — so the signal flows down the entire subtree. Because children unregister themselves from the parent when they are cancelled, forgetting to call cancel() leaves the child registered: the parent retains a reference to the child (and its resources — timers, goroutines) for as long as the parent lives. In a long-lived root context, un-cancelled children accumulate — the classic context leak. The rule 'defer cancel()' exists precisely to unregister and release immediately.

Trap

The naive answer is 'calling cancel is optional if the work finishes'. The child stays registered on the parent until cancelled — a slow leak in long-lived processes. Another trap: using context.WithTimeout without storing the cancel (withCancel(ctx, ...) style misreads) — the timer also holds the child until it fires. Candidates who describe the children map and the unregister-on-cancel behavior show real understanding.

Under The Hood — a multi-depth technical interview hub.

Third-party licenses Press ⌘ K to search.