-
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: math/rand: Expose constructor for lockedSource
#24121
Comments
Isn't this basically the same as #21393? I guess the difference is that you have a specific proposal? |
Right, this is basically a proposal that would fix #21393 if given the go-ahead. |
I think that we should consider all Go 2 math/rand proposals together to get a coherent picture. The home for that should probably be #21835. I think we should backlink here from #21835 and close this. If we do something like this, I think a better API would be: func NewLockedSource(src Source) Source Then it could be usefully used with any input source. |
One issue with considering all the math/rand proposals together is that some of them (like this one) are fully backwards compatible, while others (like switching to PCG) are not, and so have to wait for Go 2. |
On hold for eventual consideration as part of "what do we do about math/rand", as @josharian said. For now you can always put a lock around a source yourself. |
@ChimeraCoder makes an articulate argument in #25057 for how exposing a locked source could lead to less contention issues by encouraging developers not to use the global one. |
A locked source is literally a source plus a lock. It's not difficult. There's no need for this in the rand API. We don't published locked other types. |
It's not a matter of whether it's difficult or not, but what the defaults encourage. At present, And because there's neither a constructor function in the standard library for a locked source nor an interface definition, there isn't really a way for different libraries to standardize around a common interface for requiring a threadsafe source even if they want to. Either one by itself would suffice, but without either one, people reach for the most convenient default, which has led to the problems we're currently observing: a) a lot of packages duplicate the lockedSource implementation verbatim, which is a sign that people would find it valuable if it were exported b) too many packages rely on the default Source where I'm using "too many" in this context to mean "enough to provide noticeable mutex contention for applications which use these packages, even under modest workloads". |
As I wrote over in #21393, there may be some subtleties and/or optimizations that just throwing a mutex at won't help with, like batching core PRNG calls for rand.Read. But yes, it doesn't seem like a high priority. |
* By using a sync.Pool, we can preserve the current behavior for the single-threaded, serialized case (seeded with 1), but also allow concurrent callers the ability to generate random numbers with the default source without blocking on the same global mutex golang#24121 (comment) Change-Id: Icf10fdf10f96775ea64bc52202c41bf023d299de
A |
I think this is more serious than just subtleties. Anyone who copies the lockedSource implementation introduces the same race conditions fixed here and here: This demonstration in the go playground shows that a locked source copied from the standard library can interleave As @orian pointed out:
IMO this choice prevents anyone from creating a completely thread safe type LockedRand struct {
lk sync.Mutex
*rand.Rand
}
func (r *LockedRand) Seed(seed int64) {
r.lk.Lock()
r.Rand.Seed(seed)
r.lk.Unlock()
}
func (r *LockedRand) Read(p []byte) (n int, err error) {
r.lk.Lock()
n, err = r.Rand.Read(p)
r.lk.Unlock()
return n, err
} So solving this is clearly not just a However, FWIW there are comments on both rand.Read and rand.Seed claiming they cannot be used concurrently so perhaps users should start creating locked |
This proposal has been added to the active column of the proposals project |
No change in consensus, so declined. |
The
math/rand
library defines theSource
interface to represent a source of pseudorandom numbers, and a constructor returning a concrete implementation,NewSource
. However, the implementation returned byNewSource
is not safe for concurrent use by multiple goroutines. The package also defines a thread-safelockedSource
implementation, but this implementation is not exposed.The proposal is to expose a constructor for the
lockedSource
by adding the following function tomath/rand
:Benefits
Today, users who need a thread-safe implementation must duplicate the
lockedSource
implementation in their own code. Apparently over 1,000 GitHub projects have done so (search). Exposing the standard library implementation would avoid this duplicate code.Why do users need a thread-safe source? One reason is that such a source is necessary when implementing any object that (1) exposes thread-safe methods which use random numbers in their implementation and (2) supports dependency injection.
Incidentally, the proposed rand package for Go 2 (#21835) goes even further than this proposal and makes LockedSource a public type.
References
This proposal was originally raised in 2015 on golang-dev, but it appears not to have been resolved. The lack of a constructor for a
lockedSource
was raised as issue #21393.The text was updated successfully, but these errors were encountered: