-
Notifications
You must be signed in to change notification settings - Fork 18k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
x/sync/errgroup: Group: document that Go may not be concurrent with Wait unless semaphore > 0 #70284
Comments
I send a patch here https://go-review.googlesource.com/c/sync/+/627075 |
Change https://go.dev/cl/627075 mentions this issue: |
I don't think this is what you meant to write. |
It is exactly what I meant to write. Otherwise the g.Go would block. |
Your program contains a data race, because it's not valid to increment a waitgroup with 0 count while waiting on it. Your submitted patch doesn't change this. |
The waitgroup has count of 1 when the |
No. You would need additional synchronization to guarantee this.
|
@jrick is right. In your case, there is no synchronization between In general, if you have questions about how to use the API, please see https://go.dev/wiki/Questions . Thanks. |
Perhaps I misunderstand, but I think the Group is working as intended. SetLimit may prevent the Group from accepting new work, and the client must deal with that. It definitely cannot call Go asynchronously as there would be no guarantee that it happened before the later call to Wait. Perhaps the documentation could be improved, but I don't think there's a bug. |
Ah, @cherrymui beat me to it! |
This was just my failed attempt to have a simple reproducer to the issue that's still there.
|
This program has a race: the second call to Go races with Wait. Consider: the goroutine created by the first Go function could complete (along with println, Sleep, close) before the goroutine created by the |
I know it's not properly synchronized and I never said it is. I said that the order is right. I wouldn't expect runtime to stay idle during sleep and not execute the runnable goroutine. I guess a race free reproducer could be:
https://go.dev/play/p/Zg5GpZJz2bK but I have to admit, there's probably no way to reproduce the problem in a completely race free way. If |
I guess it would be cleaner if the docs were saying explicitly that |
It's fine to call Go concurrently with Wait, and indeed useful, if one item of work might add others to the queue. But you can't use SetLimit in this scenario or else the Group will stop accepting work. We could certainly document that, but we should not try to disallow concurrent calls to Go. |
It is a very unintuitive requirement which is really driven by implementation details not the best API design but you're right. |
I think your criticism of the design is unfair. The doc comment for SetLimit says that it "limits the number of active goroutines", and that "any subsequent call to the Go method will block ...". The alternative that you propose would remove the blocking behavior, which would be an incompatible change (since it removes happens-before edges), and it would require that Group maintain an arbitrarily large queue of functions provided to Go before a goroutine is available to run them, which could cause an application to use unbounded memory when dealing with very long streams of tasks. |
So I'm not advocating for my change any more at all. My criticism is about the fact that calling |
True, but the exact same principle applies to plain old WaitGroups: to add an item to the group you call Add(1) and later Done. In the simple case you make all the Add calls in sequence and all the Dones happen asynchronously. In more complex cases a task begets more tasks, so it calls Add (for the child) before calling Done for itself, preserving the invariant. But you get into trouble if you make the Add asynchronous to the Wait without otherwise ensuring that the semaphore is nonzero. Perhaps we could add some clarifying documentation. |
Yes but WaitGroup has the following in the docs:
which gives a user a chance to use the API correctly without looking into its implementation. |
BTW I came up with this issue because I found in the codebase I'm working on the following code outside of group goroutines:
and it seemed fishy from the concurrency point of view so I kept digging. Apparently someone was misled by errgroup.Group docs/API. |
Reopening as a doc change request. |
Group.Wait
may return before all Group.Go
calls are finished
I'd like to work on this issue. I'II update the documentation to clarify the behavior when the semaphore limit is set to zero |
Change https://go.dev/cl/660075 mentions this issue: |
(Edit: skip down to #70284 (comment); this is now a doc change request. --@adonovan)
Go version
go version go1.23.1 darwin/amd64
Output of
go env
in your module/workspace:What did you do?
When following code is run:
https://go.dev/play/p/xTIsT1iouTd
What did you see happen?
The program printed:
What did you expect to see?
The program printing:
The text was updated successfully, but these errors were encountered: