Contexts
Contexts are a key feature in Go for managing cancellation, deadlines, and request-scoped values across API boundaries and goroutines. The context package provides a standard way to handle timeout and cancellation signals.
Key Points
- Contexts carry deadlines, cancellation signals, and request-scoped values
- Context should be the first parameter of a function, typically named
ctx - Never store contexts in structs; pass them explicitly
- Canceling a context releases resources associated with it
- Context cancellation is cascading - children are canceled when parent is canceled
context.Background()is the root of any context tree- Always call the cancel function to avoid context leaks
Creating Contexts
Contexts can be created in multiple ways:
package main
import (
"context"
"fmt"
"time"
)
func main() {
// Background context - typically used in main
ctx := context.Background()
fmt.Println("background:", ctx)
// Context with timeout
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
fmt.Println("with timeout:", ctx)
// Context with deadline
deadline := time.Now().Add(5 * time.Second)
ctx, cancel = context.WithDeadline(context.Background(), deadline)
defer cancel()
fmt.Println("with deadline:", ctx)
// Context with cancellation
ctx, cancel = context.WithCancel(context.Background())
defer cancel()
fmt.Println("with cancel:", ctx)
}background: context.Background
with timeout: context.Background. WithDeadline(...)
with deadline: context.Background. WithDeadline(...)
with cancel: context.Background.WithCancelContext Cancellation
Contexts can be canceled to signal goroutines to stop work:
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx. Done():
fmt.Println("goroutine: context canceled")
return
default:
fmt.Println("goroutine: working...")
time.Sleep(500 * time.Millisecond)
}
}
}()
time.Sleep(2 * time.Second)
fmt.Println("main: canceling context")
cancel()
time.Sleep(1 * time.Second)
}goroutine: working...
goroutine: working...
goroutine: working...
main: canceling context
goroutine: context canceledContext With Values
Contexts can carry request-scoped data across API boundaries:
package main
import (
"context"
"fmt"
)
type userKey string
func main() {
// Store value in context
ctx := context. WithValue(context.Background(), userKey("userID"), 12345)
// Retrieve value from context
userID := ctx.Value(userKey("userID"))
fmt.Println("userID:", userID)
// Pass context to function
processRequest(ctx)
}
func processRequest(ctx context.Context) {
if userID, ok := ctx.Value(userKey("userID")).(int); ok {
fmt.Printf("processing request for user: %d\n", userID)
}
}userID: 12345
processing request for user: 12345Context Creation Patterns
| Pattern | Example | Description |
|---|---|---|
context.Background() | ctx := context.Background() | Root context, never canceled |
context.TODO() | ctx := context.TODO() | Placeholder when context is unclear |
context.WithCancel(parent) | ctx, cancel := context.WithCancel(parent) | Create cancelable context |
context.WithTimeout(parent, duration) | ctx, cancel := context.WithTimeout(parent, 5*time.Second) | Context with timeout duration |
context. WithDeadline(parent, time) | ctx, cancel := context.WithDeadline(parent, deadline) | Context with absolute deadline |
context.WithValue(parent, key, val) | ctx := context.WithValue(parent, key, value) | Context with key-value pair |
Context Operations
| Operation | Syntax | Example | Description |
|---|---|---|---|
| Done Channel | ctx.Done() | <-ctx.Done() | Returns channel closed when context canceled |
| Error | ctx.Err() | err := ctx.Err() | Returns cancellation reason |
| Deadline | ctx.Deadline() | deadline, ok := ctx.Deadline() | Returns deadline time if set |
| Value | ctx.Value(key) | val := ctx.Value(key) | Returns value for key |
| Cancel | cancel() | defer cancel() | Cancels context and children |
Context Internals
| Concept | Description | Notes |
|---|---|---|
| Immutability | Contexts are immutable | Each With* function creates a new context |
| Thread-safe | Safe for simultaneous use by multiple goroutines | All context methods are concurrent-safe |
| Cancellation propagation | Parent cancellation cancels all children | Forms a cancellation tree |
| Deadline inheritance | Child inherits parent's deadline if earlier | Cannot extend parent's deadline |
| Value lookup | Searches up the context chain | Most recent value for key is returned |
| Done channel | Closed on cancellation, never sends values | Use in select statements |
Common Patterns
| Pattern | Code | Use Case |
|---|---|---|
| HTTP request context | ctx := req.Context() | Get context from HTTP request |
| Defer cancel | ctx, cancel := context.WithTimeout(... ); defer cancel() | Always call cancel to prevent leaks |
| Select with context | select { case <-ctx.Done(): return ctx.Err(); case result := <-ch: return result } | Respect cancellation in goroutines |
| Check cancellation | if err := ctx.Err(); err != nil { return err } | Early return if context canceled |
| Custom key type | type myKey string; const userKey myKey = "user" | Avoid key collisions |
| Timeout operation | ctx, cancel := context.WithTimeout(ctx, 5*time.Second); defer cancel() | Limit operation duration |