Standard library patterns · 3 MIN READ
Every blocking call should take a context
Make a function that waits stop waiting when told to.
A context is how a caller says "stop, I no longer need this". Anything that could block — a network call, a sleep, a channel receive — should take one and should select on ctx.Done() alongside whatever it was waiting for. A function that ignores its context is a function nothing can cancel.
ctx.Err() says which happened: context.Canceled when someone called cancel, context.DeadlineExceeded when the timeout ran out. Returning it directly is usually right — the caller already knows what it asked for.
SEE THE IDEA
Cancellation has to reach the blocking operation
- 01
Caller
goctx, cancel := context.WithTimeout(...)Sets the lifetime
- 02
Wait
goselect { case <-ctx.Done(): }Observes the signal
- 03
Finish
goreturn ctx.Err()Reports cancellation or deadline
The context carries a cancellation signal; it does not interrupt work automatically.
In practice
Pass context as the first parameter and propagate it to operations that already accept one. Call the returned cancel function to release resources, even if the deadline eventually expires. Keep contexts out of long-lived structs unless an API explicitly requires that lifetime.
Watch out
Do not use context values for optional arguments or general application state. Values are for request-scoped data crossing API boundaries.
Work through the exercise
package main
import (
"context"
"time"
)
// WaitFor returns nil once d has passed, or the context's error
// if the context finishes first. It must not outlive either.
func WaitFor(ctx context.Context, d time.Duration) error {
time.Sleep(d)
return nil
}
func main() {}Trace each value and write down your reasoning. Copy the source into GopherForge or your local Go environment to compile and check it. This web page does not execute your code or send it to a compiler service.
Progress stays in this browser. No account needed.