2018-08-02 11:14:16 +02:00
|
|
|
package ticker
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2019-02-07 01:48:54 +01:00
|
|
|
// Force implements the Ticker interface, and provides a method of force-feeding
|
|
|
|
// ticks, even while paused.
|
|
|
|
type Force struct {
|
2018-08-02 11:14:16 +02:00
|
|
|
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{}
|
|
|
|
}
|
|
|
|
|
2019-02-07 01:48:54 +01:00
|
|
|
// 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
|
2018-08-02 11:14:16 +02:00
|
|
|
// the ability to force-feed events that get output by the
|
2019-02-07 01:48:54 +01:00
|
|
|
func NewForce(interval time.Duration) *Force {
|
|
|
|
m := &Force{
|
2018-08-02 11:14:16 +02:00
|
|
|
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.
|
2019-02-07 01:48:54 +01:00
|
|
|
func (m *Force) Ticks() <-chan time.Time {
|
2018-08-02 11:14:16 +02:00
|
|
|
return m.Force
|
|
|
|
}
|
|
|
|
|
2018-08-10 07:15:41 +02:00
|
|
|
// Resume starts underlying time.Ticker and causes the ticker to begin
|
2018-08-02 11:14:16 +02:00
|
|
|
// delivering scheduled events.
|
|
|
|
//
|
|
|
|
// NOTE: Part of the Ticker interface.
|
2019-02-07 01:48:54 +01:00
|
|
|
func (m *Force) Resume() {
|
2018-08-02 11:14:16 +02:00
|
|
|
atomic.StoreUint32(&m.isActive, 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pause suspends the underlying ticker, such that Ticks() stops signaling at
|
|
|
|
// regular intervals.
|
|
|
|
//
|
|
|
|
// NOTE: Part of the Ticker interface.
|
2019-02-07 01:48:54 +01:00
|
|
|
func (m *Force) Pause() {
|
2018-08-02 11:14:16 +02:00
|
|
|
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.
|
2019-02-07 01:48:54 +01:00
|
|
|
func (m *Force) Stop() {
|
2018-08-02 11:14:16 +02:00
|
|
|
m.Pause()
|
|
|
|
close(m.quit)
|
|
|
|
m.wg.Wait()
|
|
|
|
}
|