Skip to content

Commit f178608

Browse files
committed
add WaitForAllTimers and RunNextTimer
Also improve reliability of immediately firing timers.
1 parent ea1bc18 commit f178608

File tree

1 file changed

+54
-25
lines changed

1 file changed

+54
-25
lines changed

clock.go

Lines changed: 54 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,36 @@ func (m *Mock) WaitForAllTimers() time.Time {
112112
return m.now
113113
}
114114

115+
// WaitForAllTimersOnce fires all timers at least once.
116+
func (m *Mock) WaitForAllTimersOnce() time.Time {
117+
m.mu.Lock()
118+
defer m.mu.Unlock()
119+
120+
scheduled := make(map[*mockTimer]struct{}, len(m.timers))
121+
for _, t := range m.timers {
122+
scheduled[t] = struct{}{}
123+
}
124+
125+
for len(m.timers) > 0 && len(scheduled) > 0 {
126+
delete(scheduled, m.timers[0])
127+
m.fireNext()
128+
}
129+
130+
return m.now
131+
}
132+
133+
// RunNextTimer fires the next timer.
134+
func (m *Mock) RunNextTimer() time.Time {
135+
m.mu.Lock()
136+
defer m.mu.Unlock()
137+
138+
if len(m.timers) > 0 {
139+
m.fireNext()
140+
}
141+
142+
return m.now
143+
}
144+
115145
// Set sets the current time.
116146
func (m *Mock) Set(until time.Time) {
117147
m.mu.Lock()
@@ -137,7 +167,7 @@ func (m *Mock) set(until time.Time) {
137167
}
138168

139169
func (m *Mock) fireNext() {
140-
t := heap.Pop(&m.timers).(*mockTimer)
170+
t := m.timers[0]
141171
m.now = t.next
142172
t.fire()
143173

@@ -167,18 +197,11 @@ func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
167197
if d <= 0 {
168198
t.fire()
169199
} else {
170-
m.schedule(t)
200+
heap.Push(&m.timers, t)
171201
}
172202
return &Timer{mocktime: t}
173203
}
174204

175-
func (m *Mock) schedule(t *mockTimer) {
176-
if t.index >= 0 {
177-
panic("already scheduled!")
178-
}
179-
heap.Push(&m.timers, t)
180-
}
181-
182205
// Now returns the current wall time on the mock clock.
183206
func (m *Mock) Now() time.Time {
184207
m.mu.Lock()
@@ -211,7 +234,7 @@ func (m *Mock) Tick(d time.Duration) <-chan time.Time {
211234
// Ticker creates a new instance of Ticker.
212235
func (m *Mock) Ticker(d time.Duration) *Ticker {
213236
if d <= 0 {
214-
panic("ticker duration must be > 0")
237+
panic("non-positive interval for Ticker.Reset")
215238
}
216239

217240
m.mu.Lock()
@@ -230,7 +253,7 @@ func (m *Mock) Ticker(d time.Duration) *Ticker {
230253
index: -1,
231254
next: m.now.Add(d),
232255
}
233-
m.schedule(t)
256+
heap.Push(&m.timers, t)
234257
return &Ticker{C: ch, mocktime: t}
235258
}
236259

@@ -254,7 +277,7 @@ func (m *Mock) Timer(d time.Duration) *Timer {
254277
if d <= 0 {
255278
t.fire()
256279
} else {
257-
m.schedule(t)
280+
heap.Push(&m.timers, t)
258281
}
259282
return &Timer{C: ch, mocktime: t}
260283
}
@@ -300,7 +323,6 @@ func (t *Timer) Reset(d time.Duration) bool {
300323
if t.realtime != nil {
301324
return t.realtime.Reset(d)
302325
}
303-
304326
return t.mocktime.reset(d)
305327
}
306328

@@ -346,14 +368,18 @@ type mockTimer struct {
346368
}
347369

348370
func (t *mockTimer) fire() {
349-
now := t.mock.now
350-
351371
if t.d != nil {
352-
t.next = now.Add(*t.d)
353-
t.mock.schedule(t)
372+
t.next = t.mock.now.Add(*t.d)
373+
if t.index < 0 {
374+
heap.Push(&t.mock.timers, t)
375+
} else {
376+
heap.Fix(&t.mock.timers, t.index)
377+
}
378+
} else if t.index >= 0 {
379+
heap.Remove(&t.mock.timers, t.index)
354380
}
355381

356-
(t.fn)(now)
382+
(t.fn)(t.mock.now)
357383
}
358384

359385
func (t *mockTimer) stop() bool {
@@ -363,7 +389,6 @@ func (t *mockTimer) stop() bool {
363389
return false
364390
}
365391
heap.Remove(&t.mock.timers, t.index)
366-
t.index = -1
367392
return true
368393
}
369394

@@ -372,18 +397,22 @@ func (t *mockTimer) reset(dur time.Duration) bool {
372397
defer t.mock.mu.Unlock()
373398

374399
if t.d != nil {
400+
if dur <= 0 {
401+
panic("non-positive interval for Ticker.Reset")
402+
}
375403
*t.d = dur
376404
}
377405

378406
t.next = t.mock.now.Add(dur)
379-
380-
if t.index < 0 {
381-
heap.Push(&t.mock.timers, t)
382-
return false
383-
} else {
407+
wasPending := t.index >= 0
408+
if dur <= 0 {
409+
t.fire()
410+
} else if wasPending {
384411
heap.Fix(&t.mock.timers, t.index)
385-
return true
412+
} else {
413+
heap.Push(&t.mock.timers, t)
386414
}
415+
return wasPending
387416
}
388417

389418
// Sleep momentarily so that other goroutines can process.

0 commit comments

Comments
 (0)