forked from libp2p/go-libp2p
-
Notifications
You must be signed in to change notification settings - Fork 0
/
mockclock.go
128 lines (111 loc) · 2.52 KB
/
mockclock.go
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
package test
import (
"sort"
"sync"
"time"
)
type MockClock struct {
mu sync.Mutex
now time.Time
timers []*mockInstantTimer
advanceBySem chan struct{}
}
type mockInstantTimer struct {
c *MockClock
mu sync.Mutex
when time.Time
active bool
ch chan time.Time
}
func (t *mockInstantTimer) Ch() <-chan time.Time {
return t.ch
}
func (t *mockInstantTimer) Reset(d time.Time) bool {
t.mu.Lock()
defer t.mu.Unlock()
wasActive := t.active
t.active = true
t.when = d
// Schedule any timers that need to run. This will run this timer if t.when is before c.now
go t.c.AdvanceBy(0)
return wasActive
}
func (t *mockInstantTimer) Stop() bool {
t.mu.Lock()
defer t.mu.Unlock()
wasActive := t.active
t.active = false
return wasActive
}
func NewMockClock() *MockClock {
return &MockClock{now: time.Unix(0, 0), advanceBySem: make(chan struct{}, 1)}
}
func (c *MockClock) InstantTimer(when time.Time) *mockInstantTimer {
c.mu.Lock()
defer c.mu.Unlock()
t := &mockInstantTimer{
c: c,
when: when,
ch: make(chan time.Time, 1),
active: true,
}
c.timers = append(c.timers, t)
return t
}
// Since implements autorelay.ClockWithInstantTimer
func (c *MockClock) Since(t time.Time) time.Duration {
c.mu.Lock()
defer c.mu.Unlock()
return c.now.Sub(t)
}
func (c *MockClock) Now() time.Time {
c.mu.Lock()
defer c.mu.Unlock()
return c.now
}
func (c *MockClock) AdvanceBy(dur time.Duration) {
c.advanceBySem <- struct{}{}
defer func() { <-c.advanceBySem }()
c.mu.Lock()
now := c.now
endTime := c.now.Add(dur)
c.mu.Unlock()
// sort timers by when
if len(c.timers) > 1 {
sort.Slice(c.timers, func(i, j int) bool {
c.timers[i].mu.Lock()
c.timers[j].mu.Lock()
defer c.timers[i].mu.Unlock()
defer c.timers[j].mu.Unlock()
return c.timers[i].when.Before(c.timers[j].when)
})
}
for _, t := range c.timers {
t.mu.Lock()
if !t.active {
t.mu.Unlock()
continue
}
if !t.when.After(now) {
t.active = false
t.mu.Unlock()
// This may block if the channel is full, but that's intended. This way our mock clock never gets too far ahead of consumer.
// This also prevents us from dropping times because we're advancing too fast.
t.ch <- now
} else if !t.when.After(endTime) {
now = t.when
c.mu.Lock()
c.now = now
c.mu.Unlock()
t.active = false
t.mu.Unlock()
// This may block if the channel is full, but that's intended. See comment above
t.ch <- c.now
} else {
t.mu.Unlock()
}
}
c.mu.Lock()
c.now = endTime
c.mu.Unlock()
}