66 lines
		
	
	
		
			1.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			66 lines
		
	
	
		
			1.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package jitter
 | 
						|
 | 
						|
import (
 | 
						|
	"time"
 | 
						|
 | 
						|
	"go.unistack.org/micro/v3/util/rand"
 | 
						|
)
 | 
						|
 | 
						|
// Ticker is similar to time.Ticker but ticks at random intervals between
 | 
						|
// the min and max duration values (stored internally as int64 nanosecond
 | 
						|
// counts).
 | 
						|
type Ticker struct {
 | 
						|
	C    chan time.Time
 | 
						|
	done chan chan struct{}
 | 
						|
	min  int64
 | 
						|
	max  int64
 | 
						|
	rng  rand.Rand
 | 
						|
}
 | 
						|
 | 
						|
// NewTicker returns a pointer to an initialized instance of the Ticker.
 | 
						|
// Min and max are durations of the shortest and longest allowed
 | 
						|
// ticks. Ticker will run in a goroutine until explicitly stopped.
 | 
						|
func NewTicker(min, max time.Duration) *Ticker {
 | 
						|
	ticker := &Ticker{
 | 
						|
		C:    make(chan time.Time),
 | 
						|
		done: make(chan chan struct{}),
 | 
						|
		min:  min.Nanoseconds(),
 | 
						|
		max:  max.Nanoseconds(),
 | 
						|
	}
 | 
						|
	go ticker.run()
 | 
						|
	return ticker
 | 
						|
}
 | 
						|
 | 
						|
// Stop terminates the ticker goroutine and closes the C channel.
 | 
						|
func (ticker *Ticker) Stop() {
 | 
						|
	c := make(chan struct{})
 | 
						|
	ticker.done <- c
 | 
						|
	<-c
 | 
						|
}
 | 
						|
 | 
						|
func (ticker *Ticker) run() {
 | 
						|
	defer close(ticker.C)
 | 
						|
	t := time.NewTimer(ticker.nextInterval())
 | 
						|
	for {
 | 
						|
		// either a stop signal or a timeout
 | 
						|
		select {
 | 
						|
		case c := <-ticker.done:
 | 
						|
			t.Stop()
 | 
						|
			close(c)
 | 
						|
			return
 | 
						|
		case <-t.C:
 | 
						|
			select {
 | 
						|
			case ticker.C <- time.Now():
 | 
						|
				t.Stop()
 | 
						|
				t = time.NewTimer(ticker.nextInterval())
 | 
						|
			default:
 | 
						|
				// there could be noone receiving...
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (ticker *Ticker) nextInterval() time.Duration {
 | 
						|
	return time.Duration(ticker.rng.Int63n(ticker.max-ticker.min)+ticker.min) * time.Nanosecond
 | 
						|
}
 |