golang/go

math/rand: no non-default concurrency-safe Source

krancour opened this issue · 8 comments

I apologize if this is a duplicate issue. I looked and couldn't quite find this one...

According to math/rand package documentation, the following is not concurrency safe:

var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))

NewSource returns a new pseudo-random Source seeded with the given value. Unlike the default Source used by top-level functions, this source is not safe for concurrent use by multiple goroutines.

The aforementioned "default source," is (rightfully) not exported by the math/rand package. So the only way to generate random numbers in a concurrency-safe fashion (with this package, at least) is to resort to using package level functions that rely on the default, concurrency-safe source.

To seed that, one must make a call similar to the following:

rand.Seed(time.Now().UnixNano())

But this is equally a problem! Why? Because nothing prevents any other bit of code from also seeding/re-seeding the math/rand package-level default source. Consider the possibility that some third party package that is initialized after one has seeded the source re-seeds the source with a constant-- bye, bye, psuedo-randomness; hello determinism.

The bottom line here is that achieving concurrency safety with math/rand forces one down a path that's dependent on package-level globals and that introduces its own problems. It's out of the frying pan and into the fryer, and that is (for me at least) making the package nearly unusable.

It seems straightforward to wrap a mutex around your calls to a Source.

That said, do you have a suggestion for how the package should be changed?

In Go 2 world, we could have the default source be non-concurrency-safe, and provide a convenience wrapper that accepts a Source and returns a concurrency-safe Source. (There may be some subtleties and/or optimizations that just throwing a mutex won't help with, like batching core PRNG calls for rand.Read.)

(I have a partially written Go 2 math/rand proposal. I had in mind this being a part of it, but I hadn't gotten to working this part of it yet.)

It seems straightforward to wrap a mutex around your calls to a Source

I suppose I could and probably will. But, should I have to? imho if something in the standard library implicitly requires me to wrap something in a mutex just to use it safely, then it should have done that for me. (This is, in fact how the package level default source works. It's a locking source that utilizes a mutex. At minimum, it would be nice if that source impl or something similar to it were exported so I could use it.)

Change https://golang.org/cl/10161 mentions this issue: x/exp/rand: new rand package

Would a deterministic parallel random number generator work for you?
E.g., http://supertech.csail.mit.edu/papers/dprng.pdf

I suppose I could and probably will. But, should I have to? imho if something in the standard library implicitly requires me to wrap something in a mutex just to use it safely, then it should have done that for me. (This is, in fact how the package level default source works. It's a locking source that utilizes a mutex. At minimum, it would be nice if that source impl or something similar to it were exported so I could use it.)

There are a lot of constructs in the standard library (and language) that require you to wrap them in a mutex or use channels in order to avoid creating race conditions.

It would be helpful to have your desired change in a proposal format (https://golang.org/s/proposal) so that we can better understand what exact changes we’d make along with motivating examples on why the status quo is unusable for you.

I think I've stated the case clearly and succinctly. I'm not sure what information is missing (warranting the "WaitingForInfo" label) or what the benefit of a more elaborate proposal would be.

There is precedent with the log package. Moving this to a NeedsDecision.