Most people will not see the need for cancellation - until they either need to implement a deadline timeout later on, or run into the problem that lots of outdated tasks take up their memory. Those things can come up years after the inital program had been written.
It's also not purely a Go thing. E.g. in Java thread interruptions are also barely handled correct. Or CancellationTokens in C#.
so in your example it should be done like this: ch <- workFor(ctx, in)
and the 'workFor' function should be the one that gets canceled with the context.
Deep down at the very end, you would have select statement with '<-ctx.Done()' and something else that doesn't need canceling (if it needs canceling it should have taken context too).
Of course the world isn't perfect and not everything takes context right now (even in standard library), so you do have to do some workarounds every once in a while.
Where I was going with all of this was that the linked style guide mentions cases where people create buffered channels presumably because their channel writes start blocking. No buffer will make up for the case where the rate of work requested is higher than the rate at which work can be completed. What people really want in that case is the ability to get out of the channel write and return an error to the downstream system; you don't want to buffer, you want to cancel. There are many ways to accomplish the act of cancelling work, but you do have to watch your channel writes explicitly.
select {
default:
case <-ctx.Done():
return ctx.Err()
}
When there's no other channels to select with the context, just use that code block between lengthy operations to check context cancellation and return early.(If you are curious about how many extra time is wasted, it's easy to write a benchmark test for that. Last time I checked it was ~20ns when you are going the default/not-cancelled route)
if ctx.Err() != nil {
//then the context has expired or been cancelled
}
A lot of people will do things like:
This will block on the channel write even if the context becomes cancelled. Instead, you really need to be explicit about what you want to block: Now we don't wait for the work to be accepted if the caller decides it doesn't care about the job anymore.My impression from digging around the open source world is that nobody except me cares about this. So maybe I'm just crazy. But I really like not leaking goroutines on long-running programs, making Control-C behave gracefully, etc.