2015-10-21 12:15:19 -07:00
|
|
|
package common
|
|
|
|
|
2017-03-05 03:24:25 -05:00
|
|
|
import (
|
2017-12-19 16:16:16 -06:00
|
|
|
"sync"
|
2017-03-05 03:24:25 -05:00
|
|
|
"time"
|
|
|
|
)
|
2015-10-21 12:15:19 -07:00
|
|
|
|
2017-12-21 11:15:17 -05:00
|
|
|
// Ticker is a basic ticker interface.
|
|
|
|
type Ticker interface {
|
|
|
|
Chan() <-chan time.Time
|
|
|
|
Stop()
|
|
|
|
Reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultTicker wraps the stdlibs Ticker implementation.
|
|
|
|
type DefaultTicker struct {
|
|
|
|
t *time.Ticker
|
|
|
|
dur time.Duration
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewDefaultTicker returns a new DefaultTicker
|
|
|
|
func NewDefaultTicker(dur time.Duration) *DefaultTicker {
|
|
|
|
return &DefaultTicker{
|
|
|
|
time.NewTicker(dur),
|
|
|
|
dur,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Ticker
|
|
|
|
func (t *DefaultTicker) Chan() <-chan time.Time {
|
|
|
|
return t.t.C
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Ticker
|
|
|
|
func (t *DefaultTicker) Stop() {
|
|
|
|
t.t.Stop()
|
|
|
|
t.t = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Ticker
|
|
|
|
func (t *DefaultTicker) Reset() {
|
|
|
|
t.t = time.NewTicker(t.dur)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ManualTicker wraps a channel that can be manually sent on
|
|
|
|
type ManualTicker struct {
|
|
|
|
ch chan time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewManualTicker returns a new ManualTicker
|
|
|
|
func NewManualTicker(ch chan time.Time) *ManualTicker {
|
|
|
|
return &ManualTicker{
|
|
|
|
ch: ch,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Ticker
|
|
|
|
func (t *ManualTicker) Chan() <-chan time.Time {
|
|
|
|
return t.ch
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Ticker
|
|
|
|
func (t *ManualTicker) Stop() {
|
|
|
|
// noop
|
|
|
|
}
|
|
|
|
|
|
|
|
// Implements Ticker
|
|
|
|
func (t *ManualTicker) Reset() {
|
|
|
|
// noop
|
|
|
|
}
|
|
|
|
|
|
|
|
//---------------------------------------------------------------------
|
|
|
|
|
2015-10-21 12:15:19 -07:00
|
|
|
/*
|
|
|
|
RepeatTimer repeatedly sends a struct{}{} to .Ch after each "dur" period.
|
|
|
|
It's good for keeping connections alive.
|
|
|
|
A RepeatTimer must be Stop()'d or it will keep a goroutine alive.
|
|
|
|
*/
|
|
|
|
type RepeatTimer struct {
|
2017-12-19 16:16:16 -06:00
|
|
|
Ch chan time.Time
|
2015-10-21 12:15:19 -07:00
|
|
|
|
2017-12-19 16:16:16 -06:00
|
|
|
mtx sync.Mutex
|
|
|
|
name string
|
2017-12-21 11:15:17 -05:00
|
|
|
ticker Ticker
|
2017-12-19 16:16:16 -06:00
|
|
|
quit chan struct{}
|
|
|
|
wg *sync.WaitGroup
|
2015-10-21 12:15:19 -07:00
|
|
|
}
|
|
|
|
|
2017-12-21 11:15:17 -05:00
|
|
|
// NewRepeatTimer returns a RepeatTimer with the DefaultTicker.
|
2015-10-21 12:15:19 -07:00
|
|
|
func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer {
|
2017-12-21 11:15:17 -05:00
|
|
|
ticker := NewDefaultTicker(dur)
|
|
|
|
return NewRepeatTimerWithTicker(name, ticker)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewRepeatTimerWithTicker returns a RepeatTimer with the given ticker.
|
|
|
|
func NewRepeatTimerWithTicker(name string, ticker Ticker) *RepeatTimer {
|
2015-10-21 12:15:19 -07:00
|
|
|
var t = &RepeatTimer{
|
2017-12-19 16:16:16 -06:00
|
|
|
Ch: make(chan time.Time),
|
2017-12-21 11:15:17 -05:00
|
|
|
ticker: ticker,
|
2017-12-19 16:16:16 -06:00
|
|
|
quit: make(chan struct{}),
|
|
|
|
wg: new(sync.WaitGroup),
|
|
|
|
name: name,
|
2015-10-21 12:15:19 -07:00
|
|
|
}
|
2017-12-19 16:16:16 -06:00
|
|
|
t.wg.Add(1)
|
|
|
|
go t.fireRoutine(t.ticker)
|
2017-12-07 10:15:38 +01:00
|
|
|
return t
|
2015-10-21 12:15:19 -07:00
|
|
|
}
|
|
|
|
|
2017-12-21 11:15:17 -05:00
|
|
|
func (t *RepeatTimer) fireRoutine(ticker Ticker) {
|
2017-12-19 16:16:16 -06:00
|
|
|
for {
|
|
|
|
select {
|
2017-12-21 11:15:17 -05:00
|
|
|
case t_ := <-ticker.Chan():
|
2017-12-19 16:16:16 -06:00
|
|
|
t.Ch <- t_
|
|
|
|
case <-t.quit:
|
|
|
|
// needed so we know when we can reset t.quit
|
|
|
|
t.wg.Done()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-21 12:15:19 -07:00
|
|
|
// Wait the duration again before firing.
|
|
|
|
func (t *RepeatTimer) Reset() {
|
2017-12-19 16:16:16 -06:00
|
|
|
t.Stop()
|
|
|
|
|
|
|
|
t.mtx.Lock() // Lock
|
|
|
|
defer t.mtx.Unlock()
|
|
|
|
|
2017-12-21 11:15:17 -05:00
|
|
|
t.ticker.Reset()
|
2017-12-19 16:16:16 -06:00
|
|
|
t.quit = make(chan struct{})
|
|
|
|
t.wg.Add(1)
|
|
|
|
go t.fireRoutine(t.ticker)
|
2015-10-21 12:15:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// For ease of .Stop()'ing services before .Start()'ing them,
|
|
|
|
// we ignore .Stop()'s on nil RepeatTimers.
|
|
|
|
func (t *RepeatTimer) Stop() bool {
|
2017-12-19 16:16:16 -06:00
|
|
|
if t == nil {
|
2015-10-21 12:15:19 -07:00
|
|
|
return false
|
|
|
|
}
|
2017-12-19 16:16:16 -06:00
|
|
|
t.mtx.Lock() // Lock
|
|
|
|
defer t.mtx.Unlock()
|
2015-10-21 12:15:19 -07:00
|
|
|
|
2017-12-19 16:16:16 -06:00
|
|
|
exists := t.ticker != nil
|
|
|
|
if exists {
|
|
|
|
t.ticker.Stop() // does not close the channel
|
2017-03-05 03:24:25 -05:00
|
|
|
select {
|
2017-12-19 16:16:16 -06:00
|
|
|
case <-t.Ch:
|
|
|
|
// read off channel if there's anything there
|
|
|
|
default:
|
2017-03-05 03:24:25 -05:00
|
|
|
}
|
2017-12-19 16:16:16 -06:00
|
|
|
close(t.quit)
|
|
|
|
t.wg.Wait() // must wait for quit to close else we race Reset
|
2015-10-21 12:15:19 -07:00
|
|
|
}
|
2017-12-19 16:16:16 -06:00
|
|
|
return exists
|
2015-10-21 12:15:19 -07:00
|
|
|
}
|