1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package clock
import (
"sync/atomic"
"time"
)
type AdvancingClock struct {
*DeterministicClock
systemTime Clock
ticker Ticker
advanceEvery time.Duration
quit chan interface{}
running atomic.Bool
lastTick time.Time
}
// NewAdvancingClock creates a clock that, when started, advances at the same rate as the system clock but
// can also be advanced arbitrary amounts using the AdvanceTime method.
// Unlike the system clock, time does not progress smoothly but only increments when AdvancedTime is called or
// approximately after advanceEvery duration has elapsed. When advancing based on the system clock, the total time
// the system clock has advanced is added to the current time, preventing time differences from building up over time.
func NewAdvancingClock(advanceEvery time.Duration) *AdvancingClock {
now := SystemClock.Now()
return &AdvancingClock{
DeterministicClock: NewDeterministicClock(now),
systemTime: SystemClock,
advanceEvery: advanceEvery,
quit: make(chan interface{}),
lastTick: now,
}
}
func (c *AdvancingClock) Start() {
if !c.running.CompareAndSwap(false, true) {
// Already running
return
}
c.ticker = c.systemTime.NewTicker(c.advanceEvery)
go func() {
for {
select {
case now := <-c.ticker.Ch():
c.onTick(now)
case <-c.quit:
return
}
}
}()
}
func (c *AdvancingClock) Stop() {
if !c.running.CompareAndSwap(true, false) {
// Already stopped
return
}
c.quit <- nil
}
func (c *AdvancingClock) onTick(now time.Time) {
if !now.After(c.lastTick) {
// Time hasn't progressed for some reason, so do nothing
return
}
// Advance time by however long it has been since the last update.
// Ensures we don't drift from system time by more and more over time
advanceBy := now.Sub(c.lastTick)
c.AdvanceTime(advanceBy)
c.lastTick = now
}