Skip to content

Commit

Permalink
avoid multiple initialization of rand (#183)
Browse files Browse the repository at this point in the history
* avoid multiple initialization of rand

* fix my mistake of resolving the conflict

Co-authored-by: Bhautik Pipaliya <56270044+bhautikpip@users.noreply.github.com>
  • Loading branch information
shogo82148 and bhautikpip authored Apr 28, 2020
1 parent a8b2c39 commit 133f48b
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 12 deletions.
73 changes: 68 additions & 5 deletions utils/rand.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,72 @@
package utils

import (
crand "crypto/rand"
"encoding/binary"
"math/rand"
"sync"
"time"
)

func init() {
rand.Seed(time.Now().UnixNano())
// check lockedSource implements rand.Source
var _ rand.Source = (*lockedSource)(nil)
var _ rand.Source64 = (*lockedSource64)(nil)

type lockedSource struct {
mu sync.Mutex
src rand.Source
}

func (src *lockedSource) Int63() int64 {
src.mu.Lock()
defer src.mu.Unlock()
return src.src.Int63()
}

func (src *lockedSource) Seed(seed int64) {
src.mu.Lock()
defer src.mu.Unlock()
src.src.Seed(seed)
}

type lockedSource64 struct {
mu sync.Mutex
src rand.Source64
}

func (src *lockedSource64) Int63() int64 {
src.mu.Lock()
defer src.mu.Unlock()
return src.src.Int63()
}

func (src *lockedSource64) Uint64() uint64 {
src.mu.Lock()
defer src.mu.Unlock()
return src.src.Uint64()
}

func (src *lockedSource64) Seed(seed int64) {
src.mu.Lock()
defer src.mu.Unlock()
src.src.Seed(seed)
}

func newSeed() int64 {
var seed int64
if err := binary.Read(crand.Reader, binary.BigEndian, &seed); err != nil {
// fallback to timestamp
seed = time.Now().UnixNano()
}
return seed
}

func newGlobalRand() *rand.Rand {
src := rand.NewSource(newSeed())
if src64, ok := src.(rand.Source64); ok {
return rand.New(&lockedSource64{src: src64})
}
return rand.New(&lockedSource{src: src})
}

// Rand is an interface for a set of methods that return random value.
Expand All @@ -25,22 +85,25 @@ type Rand interface {
}

// DefaultRand is an implementation of Rand interface.
// It is safe for concurrent use by multiple goroutines.
type DefaultRand struct{}

var globalRand = newGlobalRand()

// Int63n returns, as an int64, a non-negative pseudo-random number in [0,n)
// from the default Source.
func (r *DefaultRand) Int63n(n int64) int64 {
return rand.Int63n(n)
return globalRand.Int63n(n)
}

// Intn returns, as an int, a non-negative pseudo-random number in [0,n)
// from the default Source.
func (r *DefaultRand) Intn(n int) int {
return rand.Intn(n)
return globalRand.Intn(n)
}

// Float64 returns, as a float64, a pseudo-random number in [0.0,1.0)
// from the default Source.
func (r *DefaultRand) Float64() float64 {
return rand.Float64()
return globalRand.Float64()
}
9 changes: 2 additions & 7 deletions utils/timer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,9 @@
package utils

import (
"math/rand"
"time"
)

func init() {
rand.Seed(time.Now().UnixNano())
}

// Timer is the same as time.Timer except that it has jitters.
// A Timer must be created with NewTimer.
type Timer struct {
Expand All @@ -27,7 +22,7 @@ type Timer struct {

// NewTimer creates a new Timer that will send the current time on its channel.
func NewTimer(d, jitter time.Duration) *Timer {
t := time.NewTimer(d - time.Duration(rand.Int63n(int64(jitter))))
t := time.NewTimer(d - time.Duration(globalRand.Int63n(int64(jitter))))

jitteredTimer := Timer{
t: t,
Expand All @@ -46,5 +41,5 @@ func (j *Timer) C() <-chan time.Time {
// Reset resets the timer.
// Reset should be invoked only on stopped or expired timers with drained channels.
func (j *Timer) Reset() {
j.t.Reset(j.d - time.Duration(rand.Int63n(int64(j.jitter))))
j.t.Reset(j.d - time.Duration(globalRand.Int63n(int64(j.jitter))))
}

0 comments on commit 133f48b

Please sign in to comment.