package ticker import ( "sync" "sync/atomic" "time" ) // Force implements the Ticker interface, and provides a method of force-feeding // ticks, even while paused. type Force struct { isActive uint32 // used atomically // Force is used to force-feed a ticks into the ticker. Useful for // debugging when trying to wake an event. Force chan time.Time ticker <-chan time.Time skip chan struct{} wg sync.WaitGroup quit chan struct{} } // A compile-time constraint to ensure Force satisfies the Ticker interface. var _ Ticker = (*Force)(nil) // NewForce returns a Force ticker, used for testing and debugging. It supports // the ability to force-feed events that get output by the func NewForce(interval time.Duration) *Force { m := &Force{ ticker: time.NewTicker(interval).C, Force: make(chan time.Time), skip: make(chan struct{}), quit: make(chan struct{}), } // Proxy the real ticks to our Force channel if we are active. m.wg.Add(1) go func() { defer m.wg.Done() for { select { case t := <-m.ticker: if atomic.LoadUint32(&m.isActive) == 0 { continue } select { case m.Force <- t: case <-m.skip: case <-m.quit: return } case <-m.quit: return } } }() return m } // Ticks returns a receive-only channel that delivers times at the ticker's // prescribed interval when active. Force-fed ticks can be delivered at any // time. // // NOTE: Part of the Ticker interface. func (m *Force) Ticks() <-chan time.Time { return m.Force } // Resume starts underlying time.Ticker and causes the ticker to begin // delivering scheduled events. // // NOTE: Part of the Ticker interface. func (m *Force) Resume() { atomic.StoreUint32(&m.isActive, 1) } // Pause suspends the underlying ticker, such that Ticks() stops signaling at // regular intervals. // // NOTE: Part of the Ticker interface. func (m *Force) Pause() { atomic.StoreUint32(&m.isActive, 0) // If the ticker fired and read isActive as true, it may still send the // tick. We'll try to send on the skip channel to drop it. select { case m.skip <- struct{}{}: default: } } // Stop suspends the underlying ticker, such that Ticks() stops signaling at // regular intervals, and permanently frees up any resources. // // NOTE: Part of the Ticker interface. func (m *Force) Stop() { m.Pause() close(m.quit) m.wg.Wait() }