github.com/vmg/backoff@v1.0.0/ticker.go (about) 1 package backoff 2 3 import ( 4 "runtime" 5 "sync" 6 "time" 7 ) 8 9 // Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff. 10 // 11 // Ticks will continue to arrive when the previous operation is still running, 12 // so operations that take a while to fail could run in quick succession. 13 type Ticker struct { 14 C <-chan time.Time 15 c chan time.Time 16 b BackOff 17 stop chan struct{} 18 stopOnce sync.Once 19 } 20 21 // NewTicker returns a new Ticker containing a channel that will send the time at times 22 // specified by the BackOff argument. Ticker is guaranteed to tick at least once. 23 // The channel is closed when Stop method is called or BackOff stops. 24 func NewTicker(b BackOff) *Ticker { 25 c := make(chan time.Time) 26 t := &Ticker{ 27 C: c, 28 c: c, 29 b: b, 30 stop: make(chan struct{}), 31 } 32 go t.run() 33 runtime.SetFinalizer(t, (*Ticker).Stop) 34 return t 35 } 36 37 // Stop turns off a ticker. After Stop, no more ticks will be sent. 38 func (t *Ticker) Stop() { 39 t.stopOnce.Do(func() { close(t.stop) }) 40 } 41 42 func (t *Ticker) run() { 43 c := t.c 44 defer close(c) 45 t.b.Reset() 46 47 // Ticker is guaranteed to tick at least once. 48 afterC := t.send(time.Now()) 49 50 for { 51 if afterC == nil { 52 return 53 } 54 55 select { 56 case tick := <-afterC: 57 afterC = t.send(tick) 58 case <-t.stop: 59 t.c = nil // Prevent future ticks from being sent to the channel. 60 return 61 } 62 } 63 } 64 65 func (t *Ticker) send(tick time.Time) <-chan time.Time { 66 select { 67 case t.c <- tick: 68 case <-t.stop: 69 return nil 70 } 71 72 next := t.b.NextBackOff() 73 if next == Stop { 74 t.Stop() 75 return nil 76 } 77 78 return time.After(next) 79 }