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