github.com/vmg/backoff@v1.0.0/ticker.go (about)

     1  package backoff
     2  
     3  import (
     4  	"runtime"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  // Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
    10  //
    11  // Ticks will continue to arrive when the previous operation is still running,
    12  // so operations that take a while to fail could run in quick succession.
    13  type Ticker struct {
    14  	C        <-chan time.Time
    15  	c        chan time.Time
    16  	b        BackOff
    17  	stop     chan struct{}
    18  	stopOnce sync.Once
    19  }
    20  
    21  // NewTicker returns a new Ticker containing a channel that will send the time at times
    22  // specified by the BackOff argument. Ticker is guaranteed to tick at least once.
    23  // The channel is closed when Stop method is called or BackOff stops.
    24  func NewTicker(b BackOff) *Ticker {
    25  	c := make(chan time.Time)
    26  	t := &Ticker{
    27  		C:    c,
    28  		c:    c,
    29  		b:    b,
    30  		stop: make(chan struct{}),
    31  	}
    32  	go t.run()
    33  	runtime.SetFinalizer(t, (*Ticker).Stop)
    34  	return t
    35  }
    36  
    37  // Stop turns off a ticker. After Stop, no more ticks will be sent.
    38  func (t *Ticker) Stop() {
    39  	t.stopOnce.Do(func() { close(t.stop) })
    40  }
    41  
    42  func (t *Ticker) run() {
    43  	c := t.c
    44  	defer close(c)
    45  	t.b.Reset()
    46  
    47  	// Ticker is guaranteed to tick at least once.
    48  	afterC := t.send(time.Now())
    49  
    50  	for {
    51  		if afterC == nil {
    52  			return
    53  		}
    54  
    55  		select {
    56  		case tick := <-afterC:
    57  			afterC = t.send(tick)
    58  		case <-t.stop:
    59  			t.c = nil // Prevent future ticks from being sent to the channel.
    60  			return
    61  		}
    62  	}
    63  }
    64  
    65  func (t *Ticker) send(tick time.Time) <-chan time.Time {
    66  	select {
    67  	case t.c <- tick:
    68  	case <-t.stop:
    69  		return nil
    70  	}
    71  
    72  	next := t.b.NextBackOff()
    73  	if next == Stop {
    74  		t.Stop()
    75  		return nil
    76  	}
    77  
    78  	return time.After(next)
    79  }