Terminating function execution if a context is cancelled

Function calls and goroutines cannot be terminated from the caller, the functions and goroutines have to support the cancellation, often via a context.Context value or a done channel.

In either case, the functions are responsible to check / monitor the context, and if cancel is requested (when the Context’s done channel is closed), return early. There isn’t an easier / automatic way.

If the task executes code in a loop, a convenient solution is to check the done channel in each iteration, and return if it’s closed. If the task is one “monolith”, the implementor is responsible to use / insert “checkpoints” at which the task can be reasonably aborted early if such cancellation is requested.

An easy way to check if the done channel is closed is to use a non-blocking select, such as:

select {
case <-ctx.Done():
    // Abort / return early
    return
default:
}

Care must be taken when the task uses other channel operations, as they may block in a nondeterministic way. Those selects should include the ctx.Done() channel too:

select {
case v := <- someChannel:
    // Do something with v
case <-ctx.Done():
    // Abort / return early
    return
}

Also be careful, because if the above receive from someChannel never blocks there is no guarantee a cancellation is properly handled, because if multiple communications can proceed in a select, one is chosen randomly (and there’s no guarantee the <-ctx.Done() is ever chosen). In such case you may combine the above 2: first do a non-blocking check for cancellation, then use a select with your channel operations and the cancel monitoring.

Leave a Comment