-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
proposal: sync/v2: new package #71076
Comments
Related Issues
Related Documentation Related Discussions (Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.) |
There is one issue I’ve encountered with the current How the |
Part of me wants to change to: type CondLocker[T any] interface {
Locker
*T
}
type Cond[T any, L CondLocker[T]] struct {
L T
}
// Random example pseudo code to prove this is exploitable
func (c *Cond[T, L]) Test() {
var p L = &c.L
p.Lock()
p.Unlock()
}
// ... this makes the zero value of a cond valid if the locker's zero value is valid (like Other part of me thinks this is too much |
I'm wondering if there's even a need to make I'm not familiar with the use cases for |
I don't fully understand this piece:
For panicking if V is not a comparable type and doing the equality check if V is a comparable type — Is the intent of this proposal to use In #47657 (comment), Ian had written:
Separately, @Merovius pointed out in #47657 (comment):
... but I'm not sure what solution this proposal is relying upon (or maybe I misunderstood the issue 😅). |
Just realized: A trivial way to get those semantics is |
This is exciting! Should |
I think the goals here include
When those goals are put together, it seems hard to avoid having I am definitely open to suggestions.
I'm not specifying an implementation, but looking at the current implementation it seems straightforward for the v2 |
I think we could in principle drop I think that in practical use a If we were designing from scratch it might make sense to make the |
since the original sync package will continue to be available with Cond, do we really need to have another copy elsewhere? |
@thepudds What I'm trying to express in |
We could have a NewPool function that initializes a pool with a constructor function.
|
I think a goal here should be that people can, with a little bit of work, change from using sync to using sync/v2. We don't want a state in which people are using both the sync and sync/v2 package with no prospect of ever moving completely to the sync/v2 package. |
I don't think we should change A Pool must return a pointer or pointer-containing object to be useful. (A Pool is a pool of references to temporary objects. Since a Pool doesn't report when objects are evicted, those references must be garbage collected, and therefore must be pointers.) If the pooled objects contain pointers, it is possible to distinguish between the zero value reference and an initialized reference. Therefore, there is no need for |
This comment has been minimized.
This comment has been minimized.
@jub0bs this will force people to store pointers to the slices which was quite a big problem with the original Pool. |
@neild's comment echos prior comments, which seems to be supported by various others. As some empirical experience supporting dropping |
What about pooling |
Before considering folding golang.org/x/sync/errgroup into sync/v2, its API should be improved. Relevant issue (and comments): #57534. |
Could it be I'm in favor of dropping |
Playing with it on the playground, this is legal syntax, but the type inference requires both type parameters, which is unfortunate. |
What exactly is the issue with just making it I also don't think there is a reason to forbid Restricting that does, IMO, nothing to help, but is unnecessarily restrictive. |
That would prevent reusing
There are other types which have pointers inside. |
The doubleDeferSandwich is complex, but if you just use |
@nalgeon Fair enough, I do see that the rationale is not really expressed in this proposal. I just edited the proposal to add a background section explaining why I think it's a good idea. Thanks for pointing that out. |
Why do you suppose that? I think that you are trying to make a slippery slope argument, but a slippery slope requires showing that the facts leading to the first step are going to lead to future steps. In this case the first step is being driven by the introduction of generics into the language. If no comparable change is introduced in the language in the future, then there is no reason for us to ever create a sync/v3 package. Conversely, if a change as large as generics is added to Go in the future (which seems unlikely), then, you're right, it may be appropriate to add a sync/v3. But it's still not a slippery slope: it's a reaction to changing circumstances. |
@valyala I'd add that it took 23 versions of Go before we got to v2s of standard library packages, which empirically proves a certain restraint. |
Why having both |
|
Why @ianlancetaylor |
Hi @aohoyd, please see “Principles for evolving the Go standard library” if you haven’t already, which includes:
My understanding is this particular proposal is about the sync package, which is a different package than sync/atomic, and under this particular proposal sync/atomic would remain sync/atomic. If sync/atomic at some point later evolved to a v2 package, my understanding is it would be sync/atomic/v2 (as suggested by the blog post quote above) and not sync/v2/atomic. I think a similar question came up late in the math/rand/v2 discussion about whether it should be math/v2/rand instead, but the conclusion there was math/rand/v2 was better, |
@aohoyd The quoted section specifically explains the difference (and it has been explained a couple of times during this discussion). Simply repeating your question is not helpful. If you have further questions, please refer to that section to explain why you are dissatisfied with the explanation. Though to be clear, I don't personally find it "confusing". I just think it's a bad name and I don't want to read bad names. And note that the type names appear more often and they appear in regular code (so they actually will be read, unlike import paths) and so having a good name for those is more important. |
This comment has been minimized.
This comment has been minimized.
Without having looked too closely in how So I'd be loathe to suggest that condition variables are removed from the API unless something remains to efficiently handle these two wakeup cases. |
This comment has been minimized.
This comment has been minimized.
@matttproud True, package |
Both forms of wakeup broadcast (wake-one, wake-all) can cause spurious waking. The question is the anticipated blast radius of the notification: wake-one could misnotify maximally one waiting goroutine, and wake-all could misnotify all waiting goroutines. Sometimes when designing a system that requires a condition variable, you know per the system's architecture (a priori) that the event associated with the variable should only wake up maximally one waiting (goroutine, thread, you-name-it). In that case, having the wake-one semantic is very helpful. From what I could tell, Tangent: What does intrigue me with
Some of these things can be implemented today with wrappers, but I suspect suboptimally due to needing to use an extra separate goroutine in certain cases. I realize that creating a linkage between |
I've found it to be an immensely useful synchronization construct, but I think it's a distraction in this discussion: The implementation of gate is trivial and uses nothing but standard channel operations, so it doesn't need to be in
The We could change that, introducing an |
There was some talk about changing the I think most people reaching for But I don't think that WaitGroup could move into to |
My takeaway from #18022 is that misuse of WaitGroups is common and it is difficult to write a proper vet check to prevent it. My preferences in order then would be:
I rank the original proposal third in my set of preferences because I really don't see the point of adding v2 just to improve two names that aren't even that bad if we're not going to also fix the other problems that can't be fixed without breaking the API. To me, if all we cared about was better names, we would replace the "WithContext" variations in every package that had context support added later with a v2. I don't think anyone thinks that would be a good idea. If v2 is worth doing it's worth doing because there's an API with a subtle flaw that can only be fixed by breaking compatibility, a la math/rand where there needed to be a whole new interface for seeding. The Source vs Source64 problem wasn't enough. It was that there was just no way to add a new algorithm as it was. In this case, the .Add/.Done API is a flaw based on the number of misuses seen in the wild. Changing Cond also strikes me as the sort of thing that would be good for sync/v2, but I don't have a proper opinion about how to do that because I've never used it. |
This comment has been minimized.
This comment has been minimized.
Package names, just like type names, appear in regular code and are being read regularly. They should be good and all of these are worse than |
@TopherGopher I don't see why we'd create a new name. The people who don't like this proposal seem to be opposed to creating a new package in general, not a v2 package in particular. Also, a v2 package sends a clear signal: it indicates that new code should use the v2 package and that the v1 package is now an outdated version. That's what we want here. We don't want two different packages. We want a newer version of the existing package.
I think it would be worth getting to the bottom of these tooling issues. Going forward you should expect more v2 packages, not fewer. FWIW gopls handles them pretty well for me. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
please try not to rehash past discussions / proposals |
You ignored my point that removing the I think most people want errgroup or similar. A much more productive way to avoid this issue is to either
I don't object to adding a |
https://github.com/sourcegraph/conc |
Proposal Details
The math/rand/v2 package has been successful. Let's consider another v2 package: sync/v2.
This is an update of #47657.
Background
The current sync package provides
Map
andPool
types. These types were designed before Go supported generics. They work with values of typeany
.Using
any
is not compile-time-type-safe. Nothing prevents using a key or value of any arbitrary type with async.Map
. Nothing prevents storing a value of any arbitrary type into async.Pool
. Go is in general a type-safe language. For all the reasons why Go map and slice types define the their key and element types,sync.Map
andsync.Pool
should as well.Also, using
any
is inefficient. Converting a non-pointer value toany
requires a memory allocation. This means that building async.Map
with a key type ofstring
requires an extra allocation for every value stored in the map. Storing a slice type in async.Pool
requires an extra allocation.These issues are easily avoided by changing
sync.Map
andsync.Pool
to be generic types.While we can't change
Map
andPool
to be generic in the current sync package because of compatibility concerns (see the discussion at #48287), we could consider adding new generic types to the existing sync package, as proposed at #47657. However, that will leave us with a confusing wart in the sync package that we can never remove. Having bothsync.Pool
and (say)sync.PoolOf
is confusing. Deprecatingsync.Pool
still leaves us with a strange name for the replacement type.Introducing a sync/v2 package will permit us to easily transition from the current package to a new package. It's true that future users will have to know to import "sync/v2" rather than "sync". However, few people write their own imports these days, and this transition is easily handled by goimports and similar tools.
Proposal
In the sync/v2 package, the following types and functions will be the same as in the current sync package:
The existing
Map
type will be replaced by a new type that takes two type parameters. Note that the originalRange
method is replaced with anAll
method that returns an iterator.The existing
Pool
type will be replaced by a new type that takes a type parameter.2025-01-04: The new version of
Pool
does not have an exportedNew
field; instead, useNewPool
to create aPool
that calls a function to return new values.Note that the originalGet
method is replaced by one that returns two results, with the second being abool
indicating whether a value was returned. This is only useful if the New field is optional: we could also change this to require the New field to be set, or to provide a default implementation that returns the zero value ofT
.The text was updated successfully, but these errors were encountered: