Can you implement your own Context type? How does the stdlib propagate cancellation from a custom parent?
Question 284HardGo 1.22 to 1.25
Yes, any type that implements the four methods is a Context, and all methods must be safe for concurrent use. The most common custom context is not written from scratch: it embeds context.Context and overrides one method, for example a context that hides values for sandboxing, or a test double.
// Hides all values but keeps cancellation and deadline.
type noValues struct{ context.Context }
func (noValues) Value(any) any { return nil }
func isolate(ctx context.Context) context.Context { return noValues{ctx} }
When you derive a child from a custom parent, propagateCancel works like this:
- It calls
parent.Value(&cancelCtxKey)(an unexported key) to find an underlying stdlib*cancelCtx. If found and itsDonechannel matches the parent'sDone(), the child registers there directly. Embedding keeps this fast path. In the example above, overridingValuebreaks it, because the lookup now returnsnil. - Otherwise, if the parent has an
AfterFunc(func()) func() boolmethod, that method is used. - Otherwise it starts one goroutine per child that waits on
parent.Done(). This works, but costs memory and scheduler work at scale.
What the interviewer is looking for: knowing that custom contexts are rarely needed, that wrapping by embedding is the idiom, and the hidden goroutine cost of the fallback path.
More on Context
- Q282How does errgroup.WithContext use context, and what is a common bug with it?
- Q283How would you merge two contexts so the result is canceled when either one is?
- Q285How do deadlines propagate across service boundaries (e.g., gRPC), and how do you manage a time budget across multiple downstream calls?
- Q286How do you implement graceful shutdown with signal.NotifyContext, and what context mistake defeats it?
- Q287How do you test context-aware code, including timeouts and cancellation?
- Q288Is a context safe for concurrent use? What happens if you read ctx.Done() from many goroutines, or call cancel concurrently?