How would you merge two contexts so the result is canceled when either one is?
Question 283HardGo 1.22 to 1.25
The standard library has no merge function, but WithCancelCause plus AfterFunc makes one cheap, without a dedicated watcher goroutine when both inputs are stdlib contexts. A typical need: a request context combined with a server shutdown context, or a stream's context combined with a per-message context.
// Merge returns a context canceled when a or b is done.
// Values and Deadline come from a only.
func Merge(a, b context.Context) (context.Context, context.CancelFunc) {
ctx, cancel := context.WithCancelCause(a)
stop := context.AfterFunc(b, func() {
cancel(context.Cause(b))
})
return ctx, func() {
stop()
cancel(context.Canceled)
}
}
func main() {
a, cancelA := context.WithCancel(context.Background())
defer cancelA()
b, cancelB := context.WithTimeout(context.Background(), 10*time.Millisecond)
defer cancelB()
m, cancel := Merge(a, b)
defer cancel()
<-m.Done()
fmt.Println(m.Err(), "|", context.Cause(m)) // context canceled | context deadline exceeded
}
Discussion points:
Err()isCanceledbecause we canceled manually.Causekeeps the real reason fromb.Deadline()reports onlya's deadline. If that matters, compute the earlier of the two and wrap the result inWithDeadline.- Value lookups see only
a. Merging values is ambiguous, which is one reason the stdlib does not provide merging. - The returned cancel must call
stop(), or the callback stays registered onb.
More on Context
- Q281How should a long-running worker loop respect cancellation? Discuss select fairness and CPU-bound loops.
- Q282How does errgroup.WithContext use context, and what is a common bug with it?
- Q284Can you implement your own Context type? How does the stdlib propagate cancellation from a custom parent?
- 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?