Contexts with deadlines

May 7, 2025

Today we’re talking about canceling contexts based on time. Arguably, this should have come first, as it’s a little simpler to work with than the explicit cancelation we talked about a week ago. But once again, I’m going in essentially alphabetical order, so it’s what we have…

func WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)

WithDeadline returns a derived context that points to the parent context but has the deadline adjusted to be no later than d. If the parent’s deadline is already earlier than d, WithDeadline(parent, d) is semantically equivalent to parent. The returned [Context.Done] channel is closed when the deadline expires, when the returned cancel function is called, or when the parent context’s Done channel is closed, whichever happens first.

Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete.

So here we have the ability to cancel a context at a fixed point in the future.

func cinderella(ctx context.Context) error {
  midnight := time.Now().Add(24 * time.Hour).Truncate(24 * time.Hour)
	ctx, cancel := context.WithDeadline(ctx, midnight)
	defer cancel()

	carriage := pumpkin.Transform()

	go toTheBall(ctx)

	<-ctx.Done() // Wait for context cancellation
	return carriage.Close()
}

What if we have multiple possible deadlines in play on the same context, and want to differentiate between them?

Then you can use the related WithDeadlineCause function:

func WithDeadlineCause

func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)

WithDeadlineCause behaves like WithDeadline but also sets the cause of the returned Context when the deadline is exceeded. The returned CancelFunc does not set the cause.

This works similarly to the WithCancelCause function recently discussed, but rather than the cancel function accepting the cause, you set the cause at the same time you set the deadline.

ctx, cancel := context.WithDeadlineCause(ctx, midnight, errors.New("magic spell expired"))

Share this

Direct to your inbox, daily. I respect your privacy .

Unsure? Browse the archive .

Related Content


Context cancelation

As already mentioned, the context.Context type serves two purposes: It manages cancelation signals, and it allows passing request-scoped data. In my opinion, these two purposes are distinct enough that they probably should be handled by two separate mechanisms. But alas, that’s not what we have. However, I think it’s still useful to think of the two uses as distinct. Most often (at least in a well-designed application), the primary purpose of a context is the propegation of cancelation signals.


Context deadlines

On Friday we looked at the context.Context interface as a whole. Now let’s go back and look at each method in greater detail. type Context type Context interface { // Deadline returns the time when work done on behalf of this context // should be canceled. Deadline returns ok==false when no deadline is // set. Successive calls to Deadline return the same results. Deadline() (deadline time.Time, ok bool) So there are two general cases here:


Contexts with timeouts

func WithTimeout func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)). Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete: func slowOperationWithTimeout(ctx context.Context) (Result, error) { ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() // releases resources if slowOperation completes before timeout elapses return slowOperation(ctx) } Not only is WithTimeout conceptually similar to WithDeadline, it literally is WithDeadline, as we can see form the source code:

Get daily content like this in your inbox!

Subscribe