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  }