(Hi, errgroup author here. Comments inline below.) On Thursday, October 25, 2018 at 7:40:43 AM UTC-4, Andrew Chambers wrote: > > @Sebastien > > The biggest difference is my package makes guarantees that when a bundle > is garbage collected the context and thus child goroutines > are cancelled. This lets you be more creative having shared ownership of > groups of goroutines, e.g. a collection of goroutines producing a lazy > infinite series on a channel, which would be safely closed once all > references to the bundle are gone. >
I don't think I understand your use-case here. If you're producing an infinite series on a channel, what prevents the bundle from being collected while some goroutine is still reading on that channel? It seems like you always need some higher-level structure to own both the bundle and the communication mechanism, and at that point you can toss a `Close` method on that structure and not need to worry about finalizers. On the other hand, relying on finalizers to clean up goroutines is itself quite risky. Per the documentation <http://go/godoc/runtime/#SetFinalizer>: - “If a cyclic structure includes a block with a finalizer, that cycle is not guaranteed to be garbage collected and the finalizer is not guaranteed to run, because there is no ordering that respects the dependencies.” - “The finalizer is scheduled to run at some arbitrary time after the program can no longer reach the object to which obj points. There is no guarantee that finalizers will run before a program exits […].” So if the thing you're storing the bundle in participates in a cycle, the goroutines might never be cancelled; if the goroutines in your bundle are actively performing work, you might waste an arbitrary amount of CPU before they finally stop. Moreover, if you don't have some explicit point of cancellation, you also don't have an explicit point at which you can block for the goroutines to return. So how will you test for goroutine leaks? With explicit cancellation it's easy: you cancel the bundle and wait for everything to return, and if one of the goroutines leaks or deadlocks then your test will hang — and you can send it SIGQUIT and look at the goroutine stacks to figure out what went wrong. In contrast, with the finalizer approach you can't distinguish between a goroutine leak and the collector just taking its time. (See also the first section of my 2018 GopherCon talk, *Rethinking Classical Concurrency Patterns* (slides) <https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view> (video) <https://www.youtube.com/watch?v=5zXAHh5tJqQ>.) errgroup seem to have a 'heavier' api, focused on aggregating errors, > If you aren't collecting errors, and *are* using synchronous functions to check for goroutine leaks, then the bundling doesn't provide much benefit over using context.WithCancel and a sync.WaitGroup separately: it drops the paired Add/Done boilerplate from the WaitGroup, and that's about it. `errgroup` has a heavier API because a thinner API doesn't provide enough benefit to justify another layer of abstraction. (See also chapter 4 of John Ousterhout's *A Philosophy of Software Design*.) > It also seems strange to me that the 'group' function in errgroup does not > accept a context which is idiomatic go. Being biased, I prefer my own > API, but actually, use whichever you prefer :) > In `errgroup`, the `ctx context.Context` parameter is omitted to reduce boilerplate: it is always the same for every function, so the caller only needs to obtain it once, and there is no need to repeat the same boilerplate at every call. Furthermore, it is sometimes useful to be able to make additional modifications (such as an additional cancellation layer or a watchdog timeout) for a subset of the goroutines in the group. So the explicit parameter would impose a cost (boilerplate) and remove a benefit (injecting finer-grained cancellation) while providing relatively little benefit of its own (a slightly lower likelihood of using the wrong context, which is already being reduced somewhat in https://golang.org/cl/134395). Plus, omitting the context parameter makes the zero `errgroup.Group` somewhat useful for aggregating errors without cancellation. (A context parameter passed in by a Group that was constructed without one would be misleading at best: it would suggest propagation of values when it only really provides cancellation. See also #28342 <https://golang.org/issue/28342>.) If the language didn't require so much boilerplate for each function parameter (see #21498 <https://golang.org/issue/21498>), the cost/benefit balance might swing back in favor of the explicit parameter. -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.