Hacker News new | past | comments | ask | show | jobs | submit login

the way to do is to pass context all the way through, until the other thing you are waiting on uses up no resources.

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.




This is a very trivial example so doesn't dive into all the complexity. There is a lot of nuance here, like whether or not you really want to do an operation in the background in the first place. Background work does means that you lose the ability to apply backpressure to the calling system, and that will cause a lot of problems under load. Even if you do want to do the operation in the background, you still want to provide a (derived) context so that you can link the background work to the initial request (at the very least, for tracing purposes). That is omitted here for simplicity.

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.


It does not need to be the background. You can check context cancellation without any goroutines by:

    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)


You may also simply write the following, which appears more elegant than the select.

  if ctx.Err() != nil {
    //then the context has expired or been cancelled
  }




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: