cancellationownership: Detect leaked cancel functions in GoSkip to content
Blog
Resources and lifecycle

cancellationownership

Checks context and signal-derived cancellation functions proved lost on a normal return path.

Tiercore
Kinddefect

What it detects

Tracks cancel functions returned by context and signal helpers and reports any successful return path that neither calls the cancel function nor transfers it to the caller.

Canceling a parent context, or receiving from the child’s Done channel, can stand in for the child’s own cancel on the paths it covers. Signal registrations from signal.NotifyContext still need their stop function.

The full list of precision boundaries, with the fixtures that pin each one, is in the cancellationownership design notes.

Checks

Check Tier Kind What it detects
release
core defect Reports derived cancel functions proved lost on a feasible normal return path.

Why this is flagged

A derived context can retain timers, memory, and references to its parent until it is canceled. Calling the cancel function promptly releases those resources and tells dependent work to stop.

Further reading: Package context.

How to fix it

Keep the cancel function returned when the context is created. Usually, call defer cancel() immediately after checking that creation succeeded, or call it explicitly at the point where the derived work is finished.

Examples

Flagged code

func work(parent context.Context) {
// gohawk: cancel function from context.WithCancel is not called on every return path
ctx, cancel := context.WithCancel(parent)
_ = cancel
doWork(ctx)
}

Accepted code

func workSafely(parent context.Context) {
ctx, cancel := context.WithCancel(parent)
defer cancel()
doWork(ctx)
}