Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Unified Diff: common/data/rand/mathrand/mathrand.go

Issue 2588213002: Use global rand.Rand instance in mathrand. (Closed)
Patch Set: Better comment. Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: common/data/rand/mathrand/mathrand.go
diff --git a/common/data/rand/mathrand/mathrand.go b/common/data/rand/mathrand/mathrand.go
index 58b48efbb418f80f5d26849655f072537549ded9..bafae196ef12aab6263c8f4b1cb603c7dcf586b8 100644
--- a/common/data/rand/mathrand/mathrand.go
+++ b/common/data/rand/mathrand/mathrand.go
@@ -10,16 +10,48 @@ package mathrand
import (
"math/rand"
+ "sync"
"golang.org/x/net/context"
)
var key = "holds a rand.Rand for mathrand"
-func newRand() *rand.Rand {
- return rand.New(rand.NewSource(rand.Int63()))
+var (
+ // globalOnce performs one-time global random variable initialization.
+ globalOnce sync.Once
+
+ // globalRandBase is the gloal *rand.Rand instance. It MUST NOT BE USED
+ // without holding globalRand's lock.
+ //
+ // globalRandBase must not be accessed directly; instead, it must be obtained
+ // through getGlobalRand to ensure that it is initialized.
+ globalRandBase *rand.Rand
+
+ // globalRand is the locking Rand implementation that wraps globalRandBase.
+ //
+ // globalRand must not be accessed directly; instead, it must be obtained
+ // through getGlobalRand to ensure that it is initialized.
+ globalRand *Locking
+)
+
+// getGlobalRand returns globalRand and its Locking wrapper. This must be used
+// instead of direct variable access in order to ensure that everything is
+// initialized.
+//
+// We use a Once to perform this initialization so that we can enable
+// applications to set the seed via rand.Seed if they wish.
+func getGlobalRand() (*Locking, *rand.Rand) {
+ globalOnce.Do(func() {
+ globalRandBase = newRand()
+ globalRand = &Locking{R: wrapped{globalRandBase}}
+ })
+ return globalRand, globalRandBase
}
+func newRand() *rand.Rand { return rand.New(rand.NewSource(rand.Int63())) }
+
+// getRand returns the Rand installed in c, or nil if no Rand is installed.
func getRand(c context.Context) Rand {
if r, ok := c.Value(&key).(Rand); ok {
return r
@@ -30,23 +62,17 @@ func getRand(c context.Context) Rand {
// Get gets a Rand from the Context. The resulting Rand is safe for concurrent
// use.
//
-// If one hasn't been set, this creates a new Rand object with a Source
-// initialized from the global randomness source provided by stdlib.
-//
-// If you want to get just a single random number, prefer to use a corresponding
-// global function instead: they know how to use math/rand global RNG directly
-// and thus are much faster in case the context doesn't have a rand.Rand
-// installed.
-//
-// Use 'Get' only if you plan to obtain a large series of random numbers.
+// If one hasn't been set, this will return a global Rand object backed by a
+// shared rand.Rand singleton. Just like in "math/rand", rand.Seed can be called
+// prior to using Get to set the seed used by this singleton.
func Get(c context.Context) Rand {
if r := getRand(c); r != nil {
return r
}
- // Generate a new Rand instance and return it. Our callers expect this to be
- // concurrency-safe.
- return wrapLocking(wrapRand(newRand()))
+ // Use the global instance.
+ gr, _ := getGlobalRand()
+ return gr
}
// Set sets the current *"math/rand".Rand object in the context.
@@ -77,124 +103,58 @@ func SetRand(c context.Context, r Rand) context.Context {
////////////////////////////////////////////////////////////////////////////////
// Top-level convenience functions mirroring math/rand package API.
//
-// They are here to optimize the case when the context doesn't have math.Rand
-// installed. Using Get(ctx).<function> in this case is semantically equivalent
-// to using global RNG, but much slower (up to 400x slower), because it creates
-// and seeds new math.Rand object on each call.
-//
-// Unfortunately since math.Rand is a struct, and its implementation is not
-// thread-safe, we can't just return some global math.Rand instance. The stdlib
-// has one, but it is private, and we can't reimplement it because stdlib does
-// some disgusting type casts to private types in math.Rand implementation, e.g:
-// https://github.com/golang/go/blob/fb3cf5c/src/math/rand/rand.go#L183
-//
-// This also makes mathrand API more similar to math/rand API.
+// This makes mathrand API more similar to math/rand API.
// Int63 returns a non-negative pseudo-random 63-bit integer as an int64
// from the source in the context or the shared global source.
-func Int63(c context.Context) int64 {
- if r := getRand(c); r != nil {
- return r.Int63()
- }
- return rand.Int63()
-}
+func Int63(c context.Context) int64 { return Get(c).Int63() }
// Uint32 returns a pseudo-random 32-bit value as a uint32 from the source in
// the context or the shared global source.
-func Uint32(c context.Context) uint32 {
- if r := getRand(c); r != nil {
- return r.Uint32()
- }
- return rand.Uint32()
-}
+func Uint32(c context.Context) uint32 { return Get(c).Uint32() }
// Int31 returns a non-negative pseudo-random 31-bit integer as an int32 from
// the source in the context or the shared global source.
-func Int31(c context.Context) int32 {
- if r := getRand(c); r != nil {
- return r.Int31()
- }
- return rand.Int31()
-}
+func Int31(c context.Context) int32 { return Get(c).Int31() }
// Int returns a non-negative pseudo-random int from the source in the context
// or the shared global source.
-func Int(c context.Context) int {
- if r := getRand(c); r != nil {
- return r.Int()
- }
- return rand.Int()
-}
+func Int(c context.Context) int { return Get(c).Int() }
// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)
// from the source in the context or the shared global source.
//
// It panics if n <= 0.
-func Int63n(c context.Context, n int64) int64 {
- if r := getRand(c); r != nil {
- return r.Int63n(n)
- }
- return rand.Int63n(n)
-}
+func Int63n(c context.Context, n int64) int64 { return Get(c).Int63n(n) }
// Int31n returns, as an int32, a non-negative pseudo-random number in [0,n)
// from the source in the context or the shared global source.
//
// It panics if n <= 0.
-func Int31n(c context.Context, n int32) int32 {
- if r := getRand(c); r != nil {
- return r.Int31n(n)
- }
- return rand.Int31n(n)
-}
+func Int31n(c context.Context, n int32) int32 { return Get(c).Int31n(n) }
// Intn returns, as an int, a non-negative pseudo-random number in [0,n) from
// the source in the context or the shared global source.
//
// It panics if n <= 0.
-func Intn(c context.Context, n int) int {
- if r := getRand(c); r != nil {
- return r.Intn(n)
- }
- return rand.Intn(n)
-}
+func Intn(c context.Context, n int) int { return Get(c).Intn(n) }
// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0) from
// the source in the context or the shared global source.
-func Float64(c context.Context) float64 {
- if r := getRand(c); r != nil {
- return r.Float64()
- }
- return rand.Float64()
-}
+func Float64(c context.Context) float64 { return Get(c).Float64() }
// Float32 returns, as a float32, a pseudo-random number in [0.0,1.0) from
// the source in the context or the shared global source.
-func Float32(c context.Context) float32 {
- if r := getRand(c); r != nil {
- return r.Float32()
- }
- return rand.Float32()
-}
+func Float32(c context.Context) float32 { return Get(c).Float32() }
// Perm returns, as a slice of n ints, a pseudo-random permutation of the
// integers [0,n) from the source in the context or the shared global source.
-func Perm(c context.Context, n int) []int {
- if r := getRand(c); r != nil {
- return r.Perm(n)
- }
- return rand.Perm(n)
-}
+func Perm(c context.Context, n int) []int { return Get(c).Perm(n) }
// Read generates len(p) random bytes from the source in the context or
// the shared global source and writes them into p. It always returns len(p)
// and a nil error.
-func Read(c context.Context, p []byte) (n int, err error) {
- if r := getRand(c); r != nil {
- return r.Read(p)
- }
- return rand.Read(p)
-}
+func Read(c context.Context, p []byte) (n int, err error) { return Get(c).Read(p) }
// NormFloat64 returns a normally distributed float64 in the range
// [-math.MaxFloat64, +math.MaxFloat64] with standard normal distribution
@@ -206,12 +166,7 @@ func Read(c context.Context, p []byte) (n int, err error) {
//
// sample = NormFloat64(ctx) * desiredStdDev + desiredMean
//
-func NormFloat64(c context.Context) float64 {
- if r := getRand(c); r != nil {
- return r.NormFloat64()
- }
- return rand.NormFloat64()
-}
+func NormFloat64(c context.Context) float64 { return Get(c).NormFloat64() }
// ExpFloat64 returns an exponentially distributed float64 in the range
// (0, +math.MaxFloat64] with an exponential distribution whose rate parameter
@@ -223,26 +178,23 @@ func NormFloat64(c context.Context) float64 {
//
// sample = ExpFloat64(ctx) / desiredRateParameter
//
-func ExpFloat64(c context.Context) float64 {
- if r := getRand(c); r != nil {
- return r.ExpFloat64()
- }
- return rand.ExpFloat64()
-}
+func ExpFloat64(c context.Context) float64 { return Get(c).ExpFloat64() }
// WithGoRand invokes the supplied "fn" while holding an exclusive lock
// for it. This can be used by callers to pull and use a *rand.Rand instance
// out of the Context safely.
//
-// The callback's r must not be retained or used outside of hte scope of the
+// The callback's r must not be retained or used outside of the scope of the
// callback.
func WithGoRand(c context.Context, fn func(r *rand.Rand) error) error {
if r := getRand(c); r != nil {
return r.WithGoRand(fn)
}
- // No Rand is installed in our Context. Generate a single-use Rand instance.
- // We don't need to wrap this at all, since the premise of this method is
- // that the result is not safe for concurrent use.
- return fn(newRand())
+ // Return our globalRandBase. We MUST hold globalRand's lock in order for this
+ // to be safe.
+ l, base := getGlobalRand()
+ l.Lock()
+ defer l.Unlock()
+ return fn(base)
}

Powered by Google App Engine
This is Rietveld 408576698