mrand: implement NewSyncRand and use that for DefaultRand, to make it thread-safe
This commit is contained in:
parent
0e64f16f03
commit
df01ccffcb
67
mrand/lockedSource.go
Normal file
67
mrand/lockedSource.go
Normal file
@ -0,0 +1,67 @@
|
||||
package mrand
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Everything in this file is taken from the math/rand package, which really
|
||||
// ought to expose lockedSource publicly.
|
||||
|
||||
func read(p []byte, int63 func() int64, readVal *int64, readPos *int8) (n int, err error) {
|
||||
pos := *readPos
|
||||
val := *readVal
|
||||
for n = 0; n < len(p); n++ {
|
||||
if pos == 0 {
|
||||
val = int63()
|
||||
pos = 7
|
||||
}
|
||||
p[n] = byte(val)
|
||||
val >>= 8
|
||||
pos--
|
||||
}
|
||||
*readPos = pos
|
||||
*readVal = val
|
||||
return
|
||||
}
|
||||
|
||||
type lockedSource struct {
|
||||
lk sync.Mutex
|
||||
src rand.Source64
|
||||
}
|
||||
|
||||
func (r *lockedSource) Int63() (n int64) {
|
||||
r.lk.Lock()
|
||||
n = r.src.Int63()
|
||||
r.lk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *lockedSource) Uint64() (n uint64) {
|
||||
r.lk.Lock()
|
||||
n = r.src.Uint64()
|
||||
r.lk.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
func (r *lockedSource) Seed(seed int64) {
|
||||
r.lk.Lock()
|
||||
r.src.Seed(seed)
|
||||
r.lk.Unlock()
|
||||
}
|
||||
|
||||
// seedPos implements Seed for a lockedSource without a race condition.
|
||||
func (r *lockedSource) seedPos(seed int64, readPos *int8) {
|
||||
r.lk.Lock()
|
||||
r.src.Seed(seed)
|
||||
*readPos = 0
|
||||
r.lk.Unlock()
|
||||
}
|
||||
|
||||
// read implements Read for a lockedSource without a race condition.
|
||||
func (r *lockedSource) read(p []byte, readVal *int64, readPos *int8) (n int, err error) {
|
||||
r.lk.Lock()
|
||||
n, err = read(p, r.src.Int63, readVal, readPos)
|
||||
r.lk.Unlock()
|
||||
return
|
||||
}
|
@ -14,6 +14,16 @@ type Rand struct {
|
||||
*rand.Rand
|
||||
}
|
||||
|
||||
// NewSyncRand initializes and returns a new Rand instance using the given
|
||||
// Source. The returned Rand will be safe for concurrent use.
|
||||
//
|
||||
// This will panic if the given Source doesn't implement rand.Source64.
|
||||
func NewSyncRand(src rand.Source) Rand {
|
||||
return Rand{
|
||||
Rand: rand.New(&lockedSource{src: src.(rand.Source64)}),
|
||||
}
|
||||
}
|
||||
|
||||
// Bytes returns n random bytes.
|
||||
func (r Rand) Bytes(n int) []byte {
|
||||
b := make([]byte, n)
|
||||
@ -67,7 +77,7 @@ func (r Rand) Element(slice interface{}, weight func(i int) uint64) interface{}
|
||||
|
||||
// DefaultRand is an instance off Rand whose methods are directly exported by
|
||||
// this package for convenience.
|
||||
var DefaultRand = Rand{Rand: rand.New(rand.NewSource(time.Now().UnixNano()))}
|
||||
var DefaultRand = NewSyncRand(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
// Methods off DefaultRand exported to the top level of this package.
|
||||
var (
|
||||
|
Loading…
Reference in New Issue
Block a user