Skip to main content
eLearner.app
Module 7 · Lesson 5 of 535/50 in the course~14 min
Module lessons (5/5)

`context.Context`: cancellation and deadlines

The context package is the de-facto standard for propagating cancellation, deadlines and scope-bound values along the call chain, across multiple goroutines. All standard "networking" packages (net/http, database/sql, os/exec) accept a context.Context as their first parameter.

The interface

Go
type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key any) any
}
  • Done(): channel that is closed when the context is cancelled.
  • Err(): reason for cancellation (context.Canceled or context.DeadlineExceeded).
  • Deadline(): optional deadline.
  • Value(): lookup of scope-bound values (use sparingly).

The four basic functions

Go
ctx := context.Background()              // radice: usalo in main, init, test
ctx := context.TODO()                    // "non so ancora": stub esplicito

ctx, cancel := context.WithCancel(parent)               // cancel manuale
ctx, cancel := context.WithTimeout(parent, d)           // scadenza relativa
ctx, cancel := context.WithDeadline(parent, t)          // scadenza assoluta

ctx := context.WithValue(parent, key, value)            // valore scope-bound

All derived forms return a cancel function: always call it (ideally with defer) to release internal resources, even if the context has already expired.

Timeout pattern

Go
func fetchUser(id string) (*User, error) {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    return queryUser(ctx, id)   // queryUser deve rispettare ctx
}

If queryUser takes more than 2 seconds, ctx.Done() is closed and the function must abort the operation.

Respecting the context: select on Done()

Go
func slowWork(ctx context.Context) error {
    for {
        select {
        case <-ctx.Done():
            return ctx.Err()      // canceled o deadline exceeded
        case <-time.After(100 * time.Millisecond):
            // un tick di lavoro
        }
    }
}

The rule: anywhere your function might block for a long time, it must handle ctx.Done(). Otherwise the context is "powered on" for nothing.

First-parameter convention

Go
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }
//        ^^^^^^^^^^^^^^^^^^^
// SEMPRE primo, mai dentro a una struct, mai opzionale
  • ctx ALWAYS as the first parameter, named ctx.
  • Never put Context as a field of a struct (very rare exceptions).
  • Never pass nil: use context.TODO() as a stub.

context.WithValue: sparingly

Go
type userKey struct{}

ctx := context.WithValue(ctx, userKey{}, currentUser)
// più sotto:
if u, ok := ctx.Value(userKey{}).(*User); ok { ... }

Guidelines:

  • ONLY for request-scoped data (request ID, trace ID, user identity).
  • NEVER for function parameters: pass them normally.
  • Keys of a custom non-exported type to avoid collisions.

Propagation along the chain

Go
func handleHTTP(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()   // context legato al ciclo di vita della richiesta
    user, err := fetchUser(ctx, r.URL.Query().Get("id"))
    if err != nil { ... }
    // ...
}

net/http automatically cancels r.Context() when the client disconnects or when the handler returns. By propagating it to all downstream calls, the whole tree of operations is cancelled in cascade: no wasted work after disconnect.

Try it

Exercise#go.m7.l5.e1
Attempts: 0Loading…

Create a context with a 1-second timeout derived from context.Background() and remember to call cancel with defer.

Loading editor…
Show hint

`context.WithTimeout(parent, d)` returns `(ctx, cancel)`; always call cancel.

Solution available after 3 attempts

Exercise#go.m7.l5.e2
Attempts: 0Loading…

Use <-ctx.Done() in a select to wait for the context to be cancelled.

Loading editor…
Show hint

`ctx.Done()` is a channel that closes when the context is cancelled.

Solution available after 3 attempts

Quiz#go.m7.l5.e3
Ready

By convention, where does the ctx parameter go in a function signature?

Go
func Fetch(???, url string) ([]byte, error)
Answer options

Recap

  • context.Context propagates cancellation, deadline and scope-bound values.
  • Background()/TODO() as the root; WithCancel/WithTimeout/WithDeadline to derive.
  • Always defer cancel() to release resources.
  • "Long" functions must select on ctx.Done() or pass ctx to standard packages.
  • Convention: ctx as the FIRST parameter, never inside a struct.
  • WithValue only for request-scoped data (request ID, trace ID).
  • In net/http, r.Context() is cancelled on client disconnect.