| package clockwork |
| |
| import ( |
| "time" |
| ) |
| |
| // Ticker provides an interface which can be used instead of directly |
| // using the ticker within the time module. The real-time ticker t |
| // provides ticks through t.C which becomes now t.Chan() to make |
| // this channel requirement definable in this interface. |
| type Ticker interface { |
| Chan() <-chan time.Time |
| Stop() |
| } |
| |
| type realTicker struct{ *time.Ticker } |
| |
| func (rt *realTicker) Chan() <-chan time.Time { |
| return rt.C |
| } |
| |
| type fakeTicker struct { |
| c chan time.Time |
| stop chan bool |
| clock FakeClock |
| period time.Duration |
| } |
| |
| func (ft *fakeTicker) Chan() <-chan time.Time { |
| return ft.c |
| } |
| |
| func (ft *fakeTicker) Stop() { |
| ft.stop <- true |
| } |
| |
| // runTickThread initializes a background goroutine to send the tick time to the ticker channel |
| // after every period. Tick events are discarded if the underlying ticker channel does not have |
| // enough capacity. |
| func (ft *fakeTicker) runTickThread() { |
| nextTick := ft.clock.Now().Add(ft.period) |
| next := ft.clock.After(ft.period) |
| go func() { |
| for { |
| select { |
| case <-ft.stop: |
| return |
| case <-next: |
| // We send the time that the tick was supposed to occur at. |
| tick := nextTick |
| // Before sending the tick, we'll compute the next tick time and star the clock.After call. |
| now := ft.clock.Now() |
| // First, figure out how many periods there have been between "now" and the time we were |
| // supposed to have trigged, then advance over all of those. |
| skipTicks := (now.Sub(tick) + ft.period - 1) / ft.period |
| nextTick = nextTick.Add(skipTicks * ft.period) |
| // Now, keep advancing until we are past now. This should happen at most once. |
| for !nextTick.After(now) { |
| nextTick = nextTick.Add(ft.period) |
| } |
| // Figure out how long between now and the next scheduled tick, then wait that long. |
| remaining := nextTick.Sub(now) |
| next = ft.clock.After(remaining) |
| // Finally, we can actually send the tick. |
| select { |
| case ft.c <- tick: |
| default: |
| } |
| } |
| } |
| }() |
| } |