Lesson 14 / 25
Propagating Cancellation
Passing ctx down and honouring it in goroutines.
Pass it down, check it in loops
Cancellation is cooperative: a context cannot stop a goroutine by force. Every function that does I/O or may block should accept a ctx and pass it to whatever it calls, for example http.NewRequestWithContext(ctx, ...) or db.QueryContext(ctx, ...), so that a single cancel at the top stops the whole tree of work. A goroutine that loops must check ctx.Done() in its select, both when waiting for input and when sending output, because a send to a consumer that has gone away would otherwise block forever. Long CPU-bound loops can check ctx.Err() != nil every so many iterations. In HTTP servers, r.Context() is cancelled when the client disconnects or the handler returns, so derive from it rather than from Background.
A producer that respects cancellation on send
Both the wait and the send are guarded by ctx.Done().
package main
import (
"context"
"fmt"
)
func numbers(ctx context.Context) <-chan int {
out := make(chan int)
go func() {
defer close(out)
for i := 0; ; i++ {
select {
case out <- i:
case <-ctx.Done():
return // consumer is gone: exit instead of blocking forever
}
}
}()
return out
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for n := range numbers(ctx) {
if n == 3 {
break
}
fmt.Println(n)
}
cancel() // stops the producer goroutine; defer would also do it
}Do not replace a request context with Background
Calling context.Background() deep inside a request handler cuts the cancellation chain: the work keeps running after the client has gone. If work must outlive the request, make that explicit (for example with context.WithoutCancel in Go 1.21+) and give it its own timeout.
Quick check: Why must a producer goroutine also select on ctx.Done() when sending?
- Channels are closed automatically on cancellation
- Sending is not allowed without a context
- ctx.Done() makes the send faster
- If the consumer stops receiving, the send would block forever and leak the goroutine
Answer
If the consumer stops receiving, the send would block forever and leak the goroutine — Cancellation is cooperative; every blocking point must be able to observe it.