blob: 32b5d01e7544abdee92c33bc0abf7cb32b2a7363 [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001package clockwork
2
3import (
4 "time"
5)
6
7// Ticker provides an interface which can be used instead of directly
8// using the ticker within the time module. The real-time ticker t
9// provides ticks through t.C which becomes now t.Chan() to make
10// this channel requirement definable in this interface.
11type Ticker interface {
12 Chan() <-chan time.Time
13 Stop()
14}
15
16type realTicker struct{ *time.Ticker }
17
18func (rt *realTicker) Chan() <-chan time.Time {
19 return rt.C
20}
21
22type fakeTicker struct {
23 c chan time.Time
24 stop chan bool
25 clock FakeClock
26 period time.Duration
27}
28
29func (ft *fakeTicker) Chan() <-chan time.Time {
30 return ft.c
31}
32
33func (ft *fakeTicker) Stop() {
34 ft.stop <- true
35}
36
37// runTickThread initializes a background goroutine to send the tick time to the ticker channel
38// after every period. Tick events are discarded if the underlying ticker channel does not have
39// enough capacity.
40func (ft *fakeTicker) runTickThread() {
41 nextTick := ft.clock.Now().Add(ft.period)
42 next := ft.clock.After(ft.period)
43 go func() {
44 for {
45 select {
46 case <-ft.stop:
47 return
48 case <-next:
49 // We send the time that the tick was supposed to occur at.
50 tick := nextTick
51 // Before sending the tick, we'll compute the next tick time and star the clock.After call.
52 now := ft.clock.Now()
53 // First, figure out how many periods there have been between "now" and the time we were
54 // supposed to have trigged, then advance over all of those.
55 skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period
56 nextTick = nextTick.Add(skipTicks * ft.period)
57 // Now, keep advancing until we are past now. This should happen at most once.
58 for !nextTick.After(now) {
59 nextTick = nextTick.Add(ft.period)
60 }
61 // Figure out how long between now and the next scheduled tick, then wait that long.
62 remaining := nextTick.Sub(now)
63 next = ft.clock.After(remaining)
64 // Finally, we can actually send the tick.
65 select {
66 case ft.c <- tick:
67 default:
68 }
69 }
70 }
71 }()
72}