93 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			93 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package jitter // import "go.unistack.org/micro/v3/util/jitter"
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"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 {
 | |
| 	ctx  context.Context
 | |
| 	done chan chan struct{}
 | |
| 	C    chan time.Time
 | |
| 	min  int64
 | |
| 	max  int64
 | |
| 	exp  int64
 | |
| 	exit bool
 | |
| 	rng  rand.Rand
 | |
| }
 | |
| 
 | |
| // NewTickerContext returns a pointer to an initialized instance of the Ticker.
 | |
| // It works like NewTicker except that it has ability to close via context.
 | |
| // Also it works fine with context.WithTimeout to handle max time to run ticker.
 | |
| func NewTickerContext(ctx context.Context, min, max time.Duration) *Ticker {
 | |
| 	ticker := &Ticker{
 | |
| 		C:    make(chan time.Time),
 | |
| 		done: make(chan chan struct{}),
 | |
| 		min:  min.Nanoseconds(),
 | |
| 		max:  max.Nanoseconds(),
 | |
| 		ctx:  ctx,
 | |
| 	}
 | |
| 	go ticker.run()
 | |
| 	return ticker
 | |
| }
 | |
| 
 | |
| // 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(),
 | |
| 		ctx:  context.Background(),
 | |
| 	}
 | |
| 	go ticker.run()
 | |
| 	return ticker
 | |
| }
 | |
| 
 | |
| // Stop terminates the ticker goroutine and closes the C channel.
 | |
| func (ticker *Ticker) Stop() {
 | |
| 	if ticker.exit {
 | |
| 		return
 | |
| 	}
 | |
| 	c := make(chan struct{})
 | |
| 	ticker.done <- c
 | |
| 	<-c
 | |
| 	// close(ticker.C)
 | |
| 	ticker.exit = true
 | |
| }
 | |
| 
 | |
| func (ticker *Ticker) run() {
 | |
| 	defer close(ticker.C)
 | |
| 	t := time.NewTimer(ticker.nextInterval())
 | |
| 	for {
 | |
| 		// either a stop signal or a timeout
 | |
| 		select {
 | |
| 		case <-ticker.ctx.Done():
 | |
| 			t.Stop()
 | |
| 		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
 | |
| }
 |