Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The LUCI Authors. All rights reserved. | 1 // Copyright 2015 The LUCI Authors. All rights reserved. |
| 2 // Use of this source code is governed under the Apache License, Version 2.0 | 2 // Use of this source code is governed under the Apache License, Version 2.0 |
| 3 // that can be found in the LICENSE file. | 3 // that can be found in the LICENSE file. |
| 4 | 4 |
| 5 // Package mathrand implements a mockable interface for math/rand.Rand. | 5 // Package mathrand implements a mockable interface for math/rand.Rand. |
| 6 // | 6 // |
| 7 // It is controllable through context.Context. You should use this instead of | 7 // It is controllable through context.Context. You should use this instead of |
| 8 // math/rand directly, to allow you to make deterministic tests. | 8 // math/rand directly, to allow you to make deterministic tests. |
| 9 package mathrand | 9 package mathrand |
| 10 | 10 |
| 11 import ( | 11 import ( |
| 12 "math/rand" | 12 "math/rand" |
| 13 | 13 |
| 14 "golang.org/x/net/context" | 14 "golang.org/x/net/context" |
| 15 ) | 15 ) |
| 16 | 16 |
| 17 var key = "holds a rand.Rand for mathrand" | 17 var key = "holds a rand.Rand for mathrand" |
| 18 | 18 |
| 19 // globalRand is a global mathrand.Rand instance that is backed by a math/rand | |
| 20 // instance. It is locking, and is safe for return as a default. | |
| 21 var ( | |
| 22 // globalRandBase is the NON-LOCKING global *rand.Rand instance. | |
| 23 globalRandBase = wrapped{newRand()} | |
| 24 | |
| 25 // globalRand is a Locking wrapper around globalRandBase. globalRandBase MUST | |
| 26 // not be used without holding this lock. | |
| 27 globalRand = &Locking{R: globalRandBase} | |
| 28 ) | |
| 29 | |
| 19 func newRand() *rand.Rand { | 30 func newRand() *rand.Rand { |
| 20 return rand.New(rand.NewSource(rand.Int63())) | 31 return rand.New(rand.NewSource(rand.Int63())) |
| 21 } | 32 } |
| 22 | 33 |
| 23 func getRand(c context.Context) Rand { | 34 func getRand(c context.Context) Rand { |
| 24 if r, ok := c.Value(&key).(Rand); ok { | 35 if r, ok := c.Value(&key).(Rand); ok { |
| 25 return r | 36 return r |
| 26 } | 37 } |
| 27 return nil | 38 return nil |
| 28 } | 39 } |
| 29 | 40 |
| 30 // Get gets a Rand from the Context. The resulting Rand is safe for concurrent | 41 // Get gets a Rand from the Context. The resulting Rand is safe for concurrent |
| 31 // use. | 42 // use. |
| 32 // | 43 // |
| 33 // If one hasn't been set, this creates a new Rand object with a Source | 44 // If one hasn't been set, this creates a new Rand object with a Source |
| 34 // initialized from the global randomness source provided by stdlib. | 45 // initialized from the global randomness source provided by stdlib. |
| 35 // | 46 // |
| 36 // If you want to get just a single random number, prefer to use a corresponding | 47 // If you want to get just a single random number, prefer to use a corresponding |
| 37 // global function instead: they know how to use math/rand global RNG directly | 48 // global function instead: they know how to use math/rand global RNG directly |
| 38 // and thus are much faster in case the context doesn't have a rand.Rand | 49 // and thus are much faster in case the context doesn't have a rand.Rand |
| 39 // installed. | 50 // installed. |
| 40 // | 51 // |
| 41 // Use 'Get' only if you plan to obtain a large series of random numbers. | 52 // Use 'Get' only if you plan to obtain a large series of random numbers. |
| 42 func Get(c context.Context) Rand { | 53 func Get(c context.Context) Rand { |
| 43 if r := getRand(c); r != nil { | 54 if r := getRand(c); r != nil { |
| 44 return r | 55 return r |
| 45 } | 56 } |
| 46 | 57 |
| 47 // Generate a new Rand instance and return it. Our callers expect this t o be | 58 // Generate a new Rand instance and return it. Our callers expect this t o be |
|
Vadim Sh.
2016/12/20 01:40:38
this comment is stale now
dnj
2016/12/20 02:16:12
Done.
| |
| 48 // concurrency-safe. | 59 // concurrency-safe. |
| 49 » return wrapLocking(wrapRand(newRand())) | 60 » return globalRand |
| 50 } | 61 } |
| 51 | 62 |
| 52 // Set sets the current *"math/rand".Rand object in the context. | 63 // Set sets the current *"math/rand".Rand object in the context. |
| 53 // | 64 // |
| 54 // Useful for testing with a quick mock. The supplied *rand.Rand will be wrapped | 65 // Useful for testing with a quick mock. The supplied *rand.Rand will be wrapped |
| 55 // in a *Locking if necessary such that when it is returned from Get, it is | 66 // in a *Locking if necessary such that when it is returned from Get, it is |
| 56 // safe for concurrent use. | 67 // safe for concurrent use. |
| 57 func Set(c context.Context, mr *rand.Rand) context.Context { | 68 func Set(c context.Context, mr *rand.Rand) context.Context { |
| 58 var r Rand | 69 var r Rand |
| 59 if mr != nil { | 70 if mr != nil { |
| (...skipping 167 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 227 if r := getRand(c); r != nil { | 238 if r := getRand(c); r != nil { |
| 228 return r.ExpFloat64() | 239 return r.ExpFloat64() |
| 229 } | 240 } |
| 230 return rand.ExpFloat64() | 241 return rand.ExpFloat64() |
| 231 } | 242 } |
| 232 | 243 |
| 233 // WithGoRand invokes the supplied "fn" while holding an exclusive lock | 244 // WithGoRand invokes the supplied "fn" while holding an exclusive lock |
| 234 // for it. This can be used by callers to pull and use a *rand.Rand instance | 245 // for it. This can be used by callers to pull and use a *rand.Rand instance |
| 235 // out of the Context safely. | 246 // out of the Context safely. |
| 236 // | 247 // |
| 237 // The callback's r must not be retained or used outside of hte scope of the | 248 // The callback's r must not be retained or used outside of hte scope of the |
|
Vadim Sh.
2016/12/20 01:40:38
typo: hte scope
dnj
2016/12/20 02:16:12
Done.
| |
| 238 // callback. | 249 // callback. |
| 239 func WithGoRand(c context.Context, fn func(r *rand.Rand) error) error { | 250 func WithGoRand(c context.Context, fn func(r *rand.Rand) error) error { |
| 240 if r := getRand(c); r != nil { | 251 if r := getRand(c); r != nil { |
| 241 return r.WithGoRand(fn) | 252 return r.WithGoRand(fn) |
| 242 } | 253 } |
| 243 | 254 |
| 244 » // No Rand is installed in our Context. Generate a single-use Rand insta nce. | 255 » // Return our globalRandBase. We MUST hold globalRand's lock in order fo r this |
| 245 » // We don't need to wrap this at all, since the premise of this method i s | 256 » // to be safe. |
| 246 » // that the result is not safe for concurrent use. | 257 » globalRand.Lock() |
| 247 » return fn(newRand()) | 258 » defer globalRand.Unlock() |
| 259 » return fn(globalRandBase.Rand) | |
| 248 } | 260 } |
| OLD | NEW |