๐ŸŽ“ A1Learner
๐Ÿ”
0 of 28 lessons completed

Go Context Deadlines

A context deadline is a fixed moment at which a context ends itself. Work that holds the context can ask Err which ending happened.

A past deadline ends the context at once

The deadline sits in the year 2020, long before the program runs. Err reports the missed deadline right away.

package main

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

func main() {
	past := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
	ctx, cancel := context.WithDeadline(context.Background(), past)
	defer cancel()
	fmt.Println(ctx.Err())
	fmt.Println(ctx.Err() == context.DeadlineExceeded)
}

An early cancel beats a later deadline

The timeout gives the context a full hour. The cancel call runs first, so it decides the error.

package main

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

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), time.Hour)
	cancel()
	fmt.Println(ctx.Err())
}

A worker stops on an expired context

The worker waits in a select on Done. The deadline already passed, so the worker prints its stopped line at once.

package main

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

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

func main() {
	past := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
	ctx, cancel := context.WithDeadline(context.Background(), past)
	defer cancel()
	var wg sync.WaitGroup
	wg.Add(1)
	go worker(ctx, &wg)
	wg.Wait()
	fmt.Println("Main finished.")
}

Example

package main

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

func check(name string, ctx context.Context) {
	if ctx.Err() == context.DeadlineExceeded {
		fmt.Println(name, "ended by deadline")
		return
	}
	fmt.Println(name, "ended by cancel")
}

func main() {
	past := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
	deadlineCtx, stop := context.WithDeadline(context.Background(), past)
	defer stop()
	cancelCtx, cancel := context.WithCancel(context.Background())
	cancel()
	check("First", deadlineCtx)
	check("Second", cancelCtx)
}

Key points

  • Use context.WithDeadline to end a context at a fixed time.
  • Use context.WithTimeout to end a context after a length of time.
  • A context past its deadline returns context.DeadlineExceeded from Err.
  • The Done channel closes when the deadline passes, just as it does on a cancel.
  • An ending that happens first decides what Err reports.

In this example, main builds two ended contexts. The first carries a deadline in the year 2020, and the second ends through a cancel call. The check helper reads Err on each one and prints which ending each context met.

Note

WithDeadline and WithTimeout both return a cancel function, and calling it frees the timer early.

Try it Yourself

Edit the code, then press Run.

Running is simulated ยท Go Playground

Exercise

A context gets a WithTimeout of one hour and cancel runs right after it. What does Err print?