clockwork.go (8198B)
1 package clockwork 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 ) 8 9 // Clock provides an interface that packages can use instead of directly 10 // using the time module, so that chronology-related behavior can be tested 11 type Clock interface { 12 After(d time.Duration) <-chan time.Time 13 Sleep(d time.Duration) 14 Now() time.Time 15 Since(t time.Time) time.Duration 16 Until(t time.Time) time.Duration 17 NewTicker(d time.Duration) Ticker 18 NewTimer(d time.Duration) Timer 19 AfterFunc(d time.Duration, f func()) Timer 20 Location() *time.Location 21 } 22 23 // Timer provides an interface to a time.Timer which is testable. 24 // See https://golang.org/pkg/time/#Timer for more details on how timers work. 25 type Timer interface { 26 C() <-chan time.Time 27 Reset(d time.Duration) bool 28 Stop() bool 29 T() *time.Timer // underlying *time.Timer (nil when using a FakeClock) 30 } 31 32 func (rc *realClock) NewTimer(d time.Duration) Timer { 33 return &realTimer{time.NewTimer(d)} 34 } 35 func (rc *realClock) AfterFunc(d time.Duration, f func()) Timer { 36 return &realTimer{time.AfterFunc(d, f)} 37 } 38 39 type realTimer struct { 40 t *time.Timer 41 } 42 43 func (rt *realTimer) C() <-chan time.Time { return rt.t.C } 44 func (rt *realTimer) T() *time.Timer { return rt.t } 45 func (rt *realTimer) Reset(d time.Duration) bool { 46 return rt.t.Reset(d) 47 } 48 func (rt *realTimer) Stop() bool { 49 return rt.t.Stop() 50 } 51 52 // FakeClock provides an interface for a clock which can be 53 // manually advanced through time 54 type FakeClock interface { 55 Clock 56 // Advance advances the FakeClock to a new point in time, ensuring any existing 57 // sleepers are notified appropriately before returning 58 Advance(d time.Duration) 59 // BlockUntil will block until the FakeClock has the given number of 60 // sleepers (callers of Sleep or After) 61 BlockUntil(n int) 62 } 63 64 // NewRealClock returns a Clock which simply delegates calls to the actual time 65 // package; it should be used by packages in production. 66 func NewRealClock() Clock { 67 return &realClock{} 68 } 69 70 // NewRealClockInLocation ... 71 func NewRealClockInLocation(location *time.Location) Clock { 72 return &realClock{loc: location} 73 } 74 75 // NewFakeClock returns a FakeClock implementation which can be 76 // manually advanced through time for testing. The initial time of the 77 // FakeClock will be an arbitrary non-zero time. 78 func NewFakeClock() FakeClock { 79 // use a fixture that does not fulfill Time.IsZero() 80 return NewFakeClockAt(time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC)) 81 } 82 83 // NewFakeClockAt returns a FakeClock initialised at the given time.Time. 84 func NewFakeClockAt(t time.Time) FakeClock { 85 return &fakeClock{ 86 time: t, 87 } 88 } 89 90 type realClock struct { 91 loc *time.Location 92 } 93 94 func (rc *realClock) Location() *time.Location { 95 return time.Now().Location() 96 } 97 98 func (rc *realClock) After(d time.Duration) <-chan time.Time { 99 return time.After(d) 100 } 101 102 func (rc *realClock) Sleep(d time.Duration) { 103 time.Sleep(d) 104 } 105 106 func (rc *realClock) Now() time.Time { 107 if rc.loc != nil { 108 return time.Now().In(rc.loc) 109 } 110 return time.Now() 111 } 112 113 func (rc *realClock) Since(t time.Time) time.Duration { 114 return rc.Now().Sub(t) 115 } 116 117 func (rc *realClock) Until(t time.Time) time.Duration { 118 return t.Sub(rc.Now()) 119 } 120 121 func (rc *realClock) NewTicker(d time.Duration) Ticker { 122 return &realTicker{time.NewTicker(d)} 123 } 124 125 type fakeClock struct { 126 sleepers []*sleeper 127 blockers []*blocker 128 time time.Time 129 130 l sync.RWMutex 131 } 132 133 // sleeper represents a caller of After or Sleep 134 // sleeper represents a waiting timer from NewTimer, Sleep, After, etc. 135 type sleeper struct { 136 until time.Time 137 done uint32 138 callback func(interface{}, time.Time) 139 arg interface{} 140 ch chan time.Time 141 fc *fakeClock // needed for Reset() 142 } 143 144 func (s *sleeper) awaken(now time.Time) { 145 if atomic.CompareAndSwapUint32(&s.done, 0, 1) { 146 s.callback(s.arg, now) 147 } 148 } 149 func (s *sleeper) C() <-chan time.Time { return s.ch } 150 func (s *sleeper) T() *time.Timer { return nil } 151 func (s *sleeper) Reset(d time.Duration) bool { 152 active := s.Stop() 153 s.until = s.fc.Now().Add(d) 154 defer s.fc.addTimer(s) 155 defer atomic.StoreUint32(&s.done, 0) 156 return active 157 } 158 func (s *sleeper) Stop() bool { 159 stopped := atomic.CompareAndSwapUint32(&s.done, 0, 1) 160 if stopped { 161 // Expire the timer and notify blockers 162 s.until = s.fc.Now() 163 s.fc.Advance(0) 164 } 165 return stopped 166 } 167 168 // blocker represents a caller of BlockUntil 169 type blocker struct { 170 count int 171 ch chan struct{} 172 } 173 174 // After mimics time.After; it waits for the given duration to elapse on the 175 // fakeClock, then sends the current time on the returned channel. 176 func (fc *fakeClock) After(d time.Duration) <-chan time.Time { 177 return fc.NewTimer(d).C() 178 } 179 180 // NewTimer creates a new Timer that will send the current time on its channel 181 // after the given duration elapses on the fake clock. 182 func (fc *fakeClock) NewTimer(d time.Duration) Timer { 183 sendTime := func(c interface{}, now time.Time) { 184 c.(chan time.Time) <- now 185 } 186 done := make(chan time.Time, 1) 187 s := &sleeper{ 188 fc: fc, 189 until: fc.time.Add(d), 190 callback: sendTime, 191 arg: done, 192 ch: done, 193 } 194 fc.addTimer(s) 195 return s 196 } 197 198 // AfterFunc waits for the duration to elapse on the fake clock and then calls f 199 // in its own goroutine. 200 // It returns a Timer that can be used to cancel the call using its Stop method. 201 func (fc *fakeClock) AfterFunc(d time.Duration, f func()) Timer { 202 goFunc := func(fn interface{}, _ time.Time) { 203 go fn.(func())() 204 } 205 s := &sleeper{ 206 fc: fc, 207 until: fc.time.Add(d), 208 callback: goFunc, 209 arg: f, 210 // zero-valued ch, the same as it is in the `time` pkg 211 } 212 fc.addTimer(s) 213 return s 214 } 215 216 func (fc *fakeClock) addTimer(s *sleeper) { 217 fc.l.Lock() 218 defer fc.l.Unlock() 219 now := fc.time 220 if now.Sub(s.until) >= 0 { 221 // special case - trigger immediately 222 s.awaken(now) 223 } else { 224 // otherwise, add to the set of sleepers 225 fc.sleepers = append(fc.sleepers, s) 226 // and notify any blockers 227 fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers)) 228 } 229 } 230 231 // notifyBlockers notifies all the blockers waiting until the 232 // given number of sleepers are waiting on the fakeClock. It 233 // returns an updated slice of blockers (i.e. those still waiting) 234 func notifyBlockers(blockers []*blocker, count int) (newBlockers []*blocker) { 235 for _, b := range blockers { 236 if b.count == count { 237 close(b.ch) 238 } else { 239 newBlockers = append(newBlockers, b) 240 } 241 } 242 return 243 } 244 245 // Sleep blocks until the given duration has passed on the fakeClock 246 func (fc *fakeClock) Sleep(d time.Duration) { 247 <-fc.After(d) 248 } 249 250 // Time returns the current time of the fakeClock 251 func (fc *fakeClock) Now() time.Time { 252 fc.l.RLock() 253 t := fc.time 254 fc.l.RUnlock() 255 return t 256 } 257 258 // Since returns the duration that has passed since the given time on the fakeClock 259 func (fc *fakeClock) Since(t time.Time) time.Duration { 260 return fc.Now().Sub(t) 261 } 262 263 // Until returns the duration until the given time on the fakeClock 264 func (fc *fakeClock) Until(t time.Time) time.Duration { 265 return t.Sub(fc.Now()) 266 } 267 268 func (fc *fakeClock) Location() *time.Location { 269 return fc.time.Location() 270 } 271 272 func (fc *fakeClock) NewTicker(d time.Duration) Ticker { 273 ft := &fakeTicker{ 274 c: make(chan time.Time, 1), 275 stop: make(chan bool, 1), 276 clock: fc, 277 period: d, 278 } 279 go ft.tick() 280 return ft 281 } 282 283 // Advance advances fakeClock to a new point in time, ensuring channels from any 284 // previous invocations of After are notified appropriately before returning 285 func (fc *fakeClock) Advance(d time.Duration) { 286 fc.l.Lock() 287 defer fc.l.Unlock() 288 end := fc.time.Add(d) 289 var newSleepers []*sleeper 290 for _, s := range fc.sleepers { 291 if end.Sub(s.until) >= 0 { 292 s.awaken(end) 293 } else { 294 newSleepers = append(newSleepers, s) 295 } 296 } 297 fc.sleepers = newSleepers 298 fc.blockers = notifyBlockers(fc.blockers, len(fc.sleepers)) 299 fc.time = end 300 } 301 302 // BlockUntil will block until the fakeClock has the given number of sleepers 303 // (callers of Sleep or After) 304 func (fc *fakeClock) BlockUntil(n int) { 305 fc.l.Lock() 306 // Fast path: current number of sleepers is what we're looking for 307 if len(fc.sleepers) == n { 308 fc.l.Unlock() 309 return 310 } 311 // Otherwise, set up a new blocker 312 b := &blocker{ 313 count: n, 314 ch: make(chan struct{}), 315 } 316 fc.blockers = append(fc.blockers, b) 317 fc.l.Unlock() 318 <-b.ch 319 }