liveBook · Manning

Task cancellation

As explained in 100 Go Mistakes and How to Avoid Them

Context cancellation is the signaling of a context's invalidation so operations observing it can stop or abort their work.

A common mistake is to pass a context to an operation but never check whether it has been canceled. In Go, a context carries a cancellation signal across API boundaries; an operation must observe that signal to stop its work. section 8.6 section 8.6.4

Choose how cancellation begins, then make the receiving operation respond:

  • Use context.WithCancel when a caller needs to signal shutdown. Call its cancel function when the work should end; a file watcher can then close its file descriptor. section 8.6.2
  • Use context.WithTimeout to stop work that is no longer useful after a deadline. Defer the returned cancel function so the context’s retained resources are released when the caller returns. section 8.6.1
  • Watch ctx.Done() and return ctx.Err() when it closes. Err distinguishes cancellation from a passed deadline. section 8.6.4

A blocking channel send or receive defeats that response: cancellation cannot interrupt the wait. Put each channel operation in a select with a ctx.Done() case. section 8.6.4

func f(ctx context.Context) error {
    // ...
    select {              #1
    case <-ctx.Done():
        return ctx.Err()
    case ch1 <- struct{}{}:
    }
 
    select {              #2
    case <-ctx.Done():
        return ctx.Err()
    case v := <-ch2:
        // ...
    }
}

Both the send and the receive can finish normally or return when the context is done. section 8.6.4

When propagation cancels too much

An HTTP handler starts an asynchronous publication with the request context, then returns the response. The request context is canceled when the response returns, so publication may stop unexpectedly. Passing context.Background() avoids that cancellation but also loses context values, such as a tracing correlation ID. section 9.1

type detach struct {                  #1
    ctx context.Context
}
 
func (d detach) Deadline() (time.Time, bool) {
    return time.Time{}, false
}
 
func (d detach) Done() <-chan struct{} {
    return nil
}
 
func (d detach) Err() error {
    return nil
}
 
func (d detach) Value(key any) any {
    return d.ctx.Value(key)           #2
}

When publication must continue and retain the request’s values, this custom context forwards Value but detaches the parent’s deadline and cancellation. Use it only when that separation is intended. section 9.1

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Traces cancellation through Done, Err, channel selects, and the methods of a custom context.
Examples
Uses a file watcher, timed position publication, blocking channel operations, and asynchronous HTTP-triggered publication.
What it assumes
Assumes familiarity with Go goroutines and channels while explaining cancellation through concrete operations.
What you can do after
Apply it via Go's context package to stop waiting operations and avoid unintentionally canceling asynchronous work.
How it connects
Connects task cancellation to deadlines, goroutines, channels, HTTP request contexts, and context values.

Also explained in

Compare how 5 books explain task cancellation →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.