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
|
*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.
|
// Bytes returns n random bytes.
|
||||||
func (r Rand) Bytes(n int) []byte {
|
func (r Rand) Bytes(n int) []byte {
|
||||||
b := make([]byte, n)
|
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
|
// DefaultRand is an instance off Rand whose methods are directly exported by
|
||||||
// this package for convenience.
|
// 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.
|
// Methods off DefaultRand exported to the top level of this package.
|
||||||
var (
|
var (
|
||||||
|
Loading…
Reference in New Issue
Block a user