Go Gopher How to Go

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)
}
Output:
background: context.Background
with timeout: context.Background. WithDeadline(...)
with deadline: context.Background. WithDeadline(...)
with cancel: context.Background.WithCancel

Context 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)
}
Output:
goroutine: working...
goroutine: working... 
goroutine: working... 
main: canceling context
goroutine: context canceled

Context 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)
    }
}
Output:
userID: 12345
processing request for user: 12345

Context Creation Patterns

PatternExampleDescription
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

OperationSyntaxExampleDescription
Done Channelctx.Done()<-ctx.Done()Returns channel closed when context canceled
Errorctx.Err()err := ctx.Err()Returns cancellation reason
Deadlinectx.Deadline()deadline, ok := ctx.Deadline()Returns deadline time if set
Valuectx.Value(key)val := ctx.Value(key)Returns value for key
Cancelcancel()defer cancel()Cancels context and children

Context Internals

ConceptDescriptionNotes
ImmutabilityContexts are immutableEach With* function creates a new context
Thread-safeSafe for simultaneous use by multiple goroutinesAll context methods are concurrent-safe
Cancellation propagationParent cancellation cancels all childrenForms a cancellation tree
Deadline inheritanceChild inherits parent's deadline if earlierCannot extend parent's deadline
Value lookupSearches up the context chainMost recent value for key is returned
Done channelClosed on cancellation, never sends valuesUse in select statements

Common Patterns

PatternCodeUse Case
HTTP request contextctx := req.Context()Get context from HTTP request
Defer cancelctx, cancel := context.WithTimeout(... ); defer cancel()Always call cancel to prevent leaks
Select with contextselect { case <-ctx.Done(): return ctx.Err(); case result := <-ch: return result }Respect cancellation in goroutines
Check cancellationif err := ctx.Err(); err != nil { return err }Early return if context canceled
Custom key typetype myKey string; const userKey myKey = "user"Avoid key collisions
Timeout operationctx, cancel := context.WithTimeout(ctx, 5*time.Second); defer cancel()Limit operation duration