atombender 14 hours ago

It's not required, but eschewing it ends up going against the grain, since so much of the ecosystem is written to use contexts, including the standard library.

For example, say you instead of contexts, you use channels for cancellation. You can have a goroutine like this:

    go func() {
      for {
        select {
        case <-stop:
          return
        case <-time.After(1*time.Second):
          resp := fetchURL(url)
          processResult(resp.Body)  // Simplified, of course
        }
      }
    }()
If you want to be able to shut this goroutine down gracefully, you're going to have an issue where http.Get() may stall for a long time, preventing the goroutine from quitting.

Likewise, processResult() may be doing stuff that cannot be aborted just by closing the stop channel. You could pass the stop channel to it, but now you're just reinventing contexts.

Of course, you can choose to only use contexts where you're forced to, and invent wrappers around standard library stuff (e.g. the HTTP client), but at that point you're going pretty far to avoid them.

I do think the context is problematic. For the purposes of cancellation, it's invasive and litters the call graph with parameters and variables. Goroutines really ought to have an implicit context inherited from its parent, since everything is using it anyway.

Contexts are wildly abused for passing data around, leading to bloated contexts and situations where you can't follow the chain of data-passing without carefully reviewing the entire call graph. I always recommend not being extremely discriminating about where to pass values in context. A core principle is that it has to be something that is so pervasive that it would be egregious to pass around explicitly, such as loggers and application-wide feature flags.

schrodinger a day ago

Why do you encourage avoiding it? Afaik it's the only way to early-abort an operation since Goroutines operate in a cooperative, not preemptive, paradigm. To be very clear, I'm asking this completely in good faith looking to learn something new!

  • sapiogram 21 hours ago

    > Afaik it's the only way to early-abort an operation since Goroutines operate in a cooperative, not preemptive, paradigm.

    I'm not sure what you mean here. Preemptive/coorporative terminology refers to interrupting (not aborting) a CPU-bound task, in which case goroutines are fully preemptive on most platforms since Go 1.14, check the release notes for more info. However, this has nothing to do with context.

    If you're referring to early-aborting IO operations, then yes, that's what context is for. However, this doesn't really have anything to do with goroutines, you could do the same if the runtime was built on OS threads.

    • kbolino 20 hours ago

      Goroutines are preemptive only to the runtime scheduler. You, the application developer merely using the language, cannot directly preempt a goroutine.

      This makes goroutines effectively cooperative still from the perspective of the developer. The preemptive runtime "just" prevents things like user code starving out the garbage collector. To interrupt a goroutine, your options are generally limited to context cancelation and closing the channel or socket being read, if any. And the goroutine may still refuse to exit (or whatever else you want it to do), though that's largely up to how you code it.

      This difference is especially stark when compared with Erlang/BEAM where you can directly address, signal, and terminate its lightweight processes.

  • pjmlp a day ago

    You are expecting them to actually check the value, there is nothing preemptive.

    Another approach is special messages over a side channel.

    • deepsun a day ago

      So instead of a context you need to pass a a channel. Same problem.

      • pjmlp a day ago

        Not necessarily, that is one of the reasons OOP exists.

        Have a struct representing the set of associated activities, owning the channel.

nu11ptr a day ago

What would you use in its place? I've never had an issue with it. I use it for 1) early termination 2) carrying custom request metadata.

I don't really think it is fully the coloring problem because you can easily call non-context functions from context functions (but not other way around, so one way coloring issue), but you need to be aware the cancellation chain of course stops then.