Skip to content

context: reduce overhead of checking of cancellation  #72040

Closed
@duckbrain

Description

@duckbrain

Proposal Details

Abstract

This proposal introduces WithInterrupt, a new function in the context package that provides an atomic-based, low-overhead cancellation check for performance-sensitive code.

Background

The context package provides mechanisms for request-scoped cancellation, by checking ctx.Err() or <-ctx.Done(), but in tight loops these can introduce significant overhead.

A common workaround is to periodically check ctx.Err() using a counter rather than in every iteration, but this introduces a trade-off between responsiveness and efficiency. A better approach is to use sync/atomic to maintain a separate cancellation flag, but this both a non-obvious solution and must be implemented manually each time, though this code isn't that difficult to write once you understand the problem.

Proposal

Add WithInterrupt to the context package (implementation as an example)

// WithInterrupt returns a dirived context that pionts to the parent but has a new Done channel in a similar fasion to [WithCancel].
// The returned isDone function can be called to efficiently check the context's cancelation status.
// This is useful for tight loops that need an interrupt where checking `ctx.Err()` or `<-ctx.Done()` adds too much overhead.
//
// Like [WithCancel], canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this [Context] complete.
func WithInterrupt(parent Context) (ctx Context, isDone func() bool, cancel func()) {
	var flag uint32
	ctx, cancel = WithCancel(parent)

	go func() {
		<-ctx.Done()
		atomic.StoreUint32(&flag, 1)
	}()

	isDone = func() bool {
		return atomic.LoadUint32(&flag) != 0
	}
	return
}

Use Case Example

ctx, isDone, cancel := context.WithInterrupt(context.WithTimeout(context.Background(), 2*time.Second))
defer cancel()

for {
	if isDone() {
		return ctx.Err()
	}
	// Perform computation
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    NeedsFixThe path to resolution is known, but the work has not been done.Performance

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions