github.com/decred/dcrlnd@v0.7.6/ticker/force.go (about) 1 package ticker 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "time" 7 ) 8 9 // Force implements the Ticker interface, and provides a method of force-feeding 10 // ticks, even while paused. 11 type Force struct { 12 isActive uint32 // used atomically 13 14 // Force is used to force-feed a ticks into the ticker. Useful for 15 // debugging when trying to wake an event. 16 Force chan time.Time 17 18 ticker <-chan time.Time 19 skip chan struct{} 20 21 wg sync.WaitGroup 22 quit chan struct{} 23 } 24 25 // A compile-time constraint to ensure Force satisfies the Ticker interface. 26 var _ Ticker = (*Force)(nil) 27 28 // NewForce returns a Force ticker, used for testing and debugging. It supports 29 // the ability to force-feed events that get output by the 30 func NewForce(interval time.Duration) *Force { 31 m := &Force{ 32 ticker: time.NewTicker(interval).C, 33 Force: make(chan time.Time), 34 skip: make(chan struct{}), 35 quit: make(chan struct{}), 36 } 37 38 // Proxy the real ticks to our Force channel if we are active. 39 m.wg.Add(1) 40 go func() { 41 defer m.wg.Done() 42 for { 43 select { 44 case t := <-m.ticker: 45 if atomic.LoadUint32(&m.isActive) == 0 { 46 continue 47 } 48 49 select { 50 case m.Force <- t: 51 case <-m.skip: 52 case <-m.quit: 53 return 54 } 55 56 case <-m.quit: 57 return 58 } 59 } 60 }() 61 62 return m 63 } 64 65 // Ticks returns a receive-only channel that delivers times at the ticker's 66 // prescribed interval when active. Force-fed ticks can be delivered at any 67 // time. 68 // 69 // NOTE: Part of the Ticker interface. 70 func (m *Force) Ticks() <-chan time.Time { 71 return m.Force 72 } 73 74 // Resume starts underlying time.Ticker and causes the ticker to begin 75 // delivering scheduled events. 76 // 77 // NOTE: Part of the Ticker interface. 78 func (m *Force) Resume() { 79 atomic.StoreUint32(&m.isActive, 1) 80 } 81 82 // Pause suspends the underlying ticker, such that Ticks() stops signaling at 83 // regular intervals. 84 // 85 // NOTE: Part of the Ticker interface. 86 func (m *Force) Pause() { 87 atomic.StoreUint32(&m.isActive, 0) 88 89 // If the ticker fired and read isActive as true, it may still send the 90 // tick. We'll try to send on the skip channel to drop it. 91 select { 92 case m.skip <- struct{}{}: 93 default: 94 } 95 } 96 97 // Stop suspends the underlying ticker, such that Ticks() stops signaling at 98 // regular intervals, and permanently frees up any resources. 99 // 100 // NOTE: Part of the Ticker interface. 101 func (m *Force) Stop() { 102 m.Pause() 103 close(m.quit) 104 m.wg.Wait() 105 }