clock.go RawHistoryFolder
1package quicnettest
2
3import (
4 "sort"
5 "sync"
6 "time"
7)
8
9// Clock abstracts time so simulations can be driven by a ManualClock and stay
10// reproducible without sleeping.
11type Clock interface {
12 // Now reports the current time.
13 Now() time.Time
14 // NewTimer returns a stopped-by-default timer. Callers must Stop or Reset
15 // it before selecting on C.
16 NewTimer(d time.Duration) Timer
17}
18
19// Timer is the subset of *time.Timer the proxy scheduler needs. Deadlines are
20// absolute so that a stale duration computed before a clock jump cannot delay
21// delivery.
22type Timer interface {
23 // C is the channel the timer fires on.
24 C() <-chan time.Time
25 // Stop deactivates the timer, reporting whether it was active.
26 Stop() bool
27 // ResetAt re-arms the timer at the absolute deadline, draining a pending
28 // value first. It reports whether the timer was active before the call.
29 ResetAt(deadline time.Time) bool
30}
31
32// SystemClock is a Clock backed by wall time.
33type SystemClock struct{}
34
35// Now implements Clock.
36func (SystemClock) Now() time.Time { return time.Now() }
37
38// NewTimer implements Clock.
39func (SystemClock) NewTimer(d time.Duration) Timer {
40 return &systemTimer{timer: time.NewTimer(d)}
41}
42
43type systemTimer struct{ timer *time.Timer }
44
45func (t *systemTimer) C() <-chan time.Time { return t.timer.C }
46
47func (t *systemTimer) Stop() bool { return t.timer.Stop() }
48
49func (t *systemTimer) ResetAt(deadline time.Time) bool {
50 if !t.timer.Stop() {
51 select {
52 case <-t.timer.C:
53 default:
54 }
55 }
56 return t.timer.Reset(time.Until(deadline))
57}
58
59// ManualClock is a deterministic Clock. Time only moves when Advance is called,
60// so impairment scheduling can be asserted exactly instead of waiting on wall
61// time.
62type ManualClock struct {
63 mu sync.Mutex
64 now time.Time
65 order uint64
66 timers map[*manualTimer]struct{}
67}
68
69// NewManualClock returns a ManualClock positioned at start.
70func NewManualClock(start time.Time) *ManualClock {
71 return &ManualClock{now: start, timers: make(map[*manualTimer]struct{})}
72}
73
74// Now implements Clock.
75func (c *ManualClock) Now() time.Time {
76 c.mu.Lock()
77 defer c.mu.Unlock()
78 return c.now
79}
80
81// NewTimer implements Clock.
82func (c *ManualClock) NewTimer(d time.Duration) Timer {
83 t := &manualTimer{clock: c, ch: make(chan time.Time, 1)}
84 c.mu.Lock()
85 defer c.mu.Unlock()
86 t.arm(d)
87 return t
88}
89
90// Advance moves time forward by d and fires every timer that has come due.
91// Timers with equal deadlines fire in creation order, keeping the scheduler
92// deterministic. Advance panics when d is negative, because a manual clock
93// cannot un-fire timers.
94func (c *ManualClock) Advance(d time.Duration) {
95 if d < 0 {
96 panic("quicnettest: ManualClock.Advance requires a non-negative duration")
97 }
98 c.mu.Lock()
99 c.now = c.now.Add(d)
100 now := c.now
101 due := make([]*manualTimer, 0, len(c.timers))
102 for timer := range c.timers {
103 if !timer.when.After(now) {
104 due = append(due, timer)
105 }
106 }
107 sort.Slice(due, func(i, j int) bool {
108 if due[i].when.Equal(due[j].when) {
109 return due[i].order < due[j].order
110 }
111 return due[i].when.Before(due[j].when)
112 })
113 for _, timer := range due {
114 timer.active = false
115 delete(c.timers, timer)
116 select {
117 case timer.ch <- now:
118 default:
119 }
120 }
121 c.mu.Unlock()
122}
123
124type manualTimer struct {
125 clock *ManualClock
126 ch chan time.Time
127 when time.Time
128 order uint64
129 active bool
130}
131
132// arm schedules the timer relative to the clock's current time. The caller must
133// hold clock.mu.
134func (t *manualTimer) arm(d time.Duration) {
135 t.when = t.clock.now.Add(d)
136 t.order = t.clock.order
137 t.clock.order++
138 if d <= 0 {
139 t.active = false
140 select {
141 case t.ch <- t.clock.now:
142 default:
143 }
144 return
145 }
146 t.active = true
147 t.clock.timers[t] = struct{}{}
148}
149
150func (t *manualTimer) C() <-chan time.Time { return t.ch }
151
152func (t *manualTimer) Stop() bool {
153 c := t.clock
154 c.mu.Lock()
155 defer c.mu.Unlock()
156 if !t.active {
157 return false
158 }
159 t.active = false
160 delete(c.timers, t)
161 return true
162}
163
164func (t *manualTimer) ResetAt(deadline time.Time) bool {
165 c := t.clock
166 c.mu.Lock()
167 defer c.mu.Unlock()
168 wasActive := t.active
169 if t.active {
170 t.active = false
171 delete(c.timers, t)
172 }
173 select {
174 case <-t.ch:
175 default:
176 }
177 t.arm(deadline.Sub(c.now))
178 return wasActive
179}