All checks were successful
		
		
	
	test / test (push) Successful in 42s
				
			## Pull Request template Please, go through these steps before clicking submit on this PR. 1. Give a descriptive title to your PR. 2. Provide a description of your changes. 3. Make sure you have some relevant tests. 4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable). **PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING** Reviewed-on: #369 Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru> Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
		
			
				
	
	
		
			92 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			92 lines
		
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package 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
 | 
						|
	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, minTime, maxTime time.Duration) *Ticker {
 | 
						|
	ticker := &Ticker{
 | 
						|
		C:    make(chan time.Time),
 | 
						|
		done: make(chan chan struct{}),
 | 
						|
		min:  minTime.Nanoseconds(),
 | 
						|
		max:  maxTime.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(minTime, maxTime time.Duration) *Ticker {
 | 
						|
	ticker := &Ticker{
 | 
						|
		C:    make(chan time.Time),
 | 
						|
		done: make(chan chan struct{}),
 | 
						|
		min:  minTime.Nanoseconds(),
 | 
						|
		max:  maxTime.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
 | 
						|
}
 |