🎓 A1Learner
🔍
0 of 28 lessons completed

Go Context Cancellation

Context cancellation is a way to tell running work to stop through a shared signal. The signal travels inside a context value from one function to the next.

Cancel closes Done and sets Err

WithCancel builds the context and hands back a cancel function. One call closes Done and sets the error.

package main

import (
	"context"
	"fmt"
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	<-ctx.Done()
	fmt.Println(ctx.Err())
	fmt.Println(ctx.Err() == context.Canceled)
}

A worker stops when Done closes

The worker signals that it started, then waits in a select on Done. Main cancels after the signal and joins the worker before printing.

package main

import (
	"context"
	"fmt"
	"sync"
)

func worker(ctx context.Context, started chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	close(started)
	select {
	case <-ctx.Done():
		fmt.Println("Worker stopped.")
	}
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	started := make(chan struct{})
	var wg sync.WaitGroup
	wg.Add(1)
	go worker(ctx, started, &wg)
	<-started
	cancel()
	wg.Wait()
	fmt.Println("Main finished.")
}

One context passes through two functions

First hands the context to second without changing it. The cancel made in main still reaches the bottom call.

package main

import (
	"context"
	"fmt"
)

func second(ctx context.Context) {
	<-ctx.Done()
	fmt.Println("Second:", ctx.Err())
}

func first(ctx context.Context) {
	second(ctx)
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	cancel()
	first(ctx)
	fmt.Println("Main finished.")
}

Example

package main

import (
	"context"
	"fmt"
	"sync"
)

func stageOne(out chan<- string) {
	out <- "Stage one done"
}

func stageTwo(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	<-ctx.Done()
	fmt.Println("Stage two stopped:", ctx.Err())
}

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	out := make(chan string)
	var wg sync.WaitGroup
	wg.Add(1)
	go stageTwo(ctx, &wg)
	go stageOne(out)
	fmt.Println(<-out)
	cancel()
	wg.Wait()
	fmt.Println("Main finished.")
}

Key points

  • A context is a value that carries a stop signal through a program.
  • Use context.WithCancel to build a context that code can cancel by hand.
  • WithCancel returns the context and a cancel function.
  • Calling the cancel function closes the Done channel of the context.
  • The Err method of a cancelled context returns context.Canceled.
  • A worker can wait on Done inside a select statement.

In this example, main starts a second stage that waits on the Done channel. The first stage hands its line to main through a channel, and main prints it. Main then cancels the context, so the second stage prints its stopped line. The WaitGroup join keeps the main line last.

Note

Calling the cancel function more than once is safe.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Go Playground

Exercise

What happens when the cancel function of a context is called?