Lesson 13 / 25

context.Context Basics

WithCancel, WithTimeout and WithDeadline.

A tree of contexts

A context.Context exposes Done() (a channel closed when the context is cancelled or times out), Err() (nil while active, then context.Canceled or context.DeadlineExceeded), Deadline() and Value(key). Roots come from context.Background() (in main, tests and initialisation) or context.TODO() (a placeholder while refactoring). Derived contexts form a tree: WithCancel(parent) returns a child and a cancel function, WithTimeout(parent, d) cancels after a duration, and WithDeadline(parent, t) at a fixed time. Cancelling a parent cancels all its descendants, never the other way round. Always call the returned cancel function, usually with defer cancel(), to release resources as soon as the work is done; go vet warns when a cancel function is lost. Newer additions such as WithCancelCause, AfterFunc and WithoutCancel exist in recent versions (check the docs).

One signal, the whole call tree

A context carries cancellation, deadlines and request-scoped values from a caller down to every function and goroutine it starts.

Three ideas: creating contexts, propagating cancellation, and the rules for context values.
Figure 5.1: a root context branching into derived contexts.

A slow operation bounded by a timeout

The function returns as soon as the context is done.

package main

import (
	"context"
	"errors"
	"fmt"
	"time"
)

func slowQuery(ctx context.Context) (string, error) {
	select {
	case <-time.After(2 * time.Second): // simulated slow work
		return "rows", nil
	case <-ctx.Done():
		return "", ctx.Err()
	}
}

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
	defer cancel() // always release the context's resources

	_, err := slowQuery(ctx)
	if errors.Is(err, context.DeadlineExceeded) {
		fmt.Println("query timed out")
	}
}

A recall notice down the supply chain

When a manufacturer issues a recall, every distributor and shop that received the product hears about it. A shop recalling its own stock does not affect the manufacturer. Cancellation flows from parent to children only.

Quick check: What does ctx.Err() return after a WithTimeout context expires?

  • context.Canceled
  • context.DeadlineExceeded
  • nil
  • An error created by time.After
Answer

context.DeadlineExceeded — Explicit cancel() yields context.Canceled; an expired deadline yields context.DeadlineExceeded.