gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

ticker.go (2121B)


      1 package backoff
      2 
      3 import (
      4 	"context"
      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 	ctx      context.Context
     18 	timer    Timer
     19 	stop     chan struct{}
     20 	stopOnce sync.Once
     21 }
     22 
     23 // NewTicker returns a new Ticker containing a channel that will send
     24 // the time at times specified by the BackOff argument. Ticker is
     25 // guaranteed to tick at least once.  The channel is closed when Stop
     26 // method is called or BackOff stops. It is not safe to manipulate the
     27 // provided backoff policy (notably calling NextBackOff or Reset)
     28 // while the ticker is running.
     29 func NewTicker(b BackOff) *Ticker {
     30 	return NewTickerWithTimer(b, &defaultTimer{})
     31 }
     32 
     33 // NewTickerWithTimer returns a new Ticker with a custom timer.
     34 // A default timer that uses system timer is used when nil is passed.
     35 func NewTickerWithTimer(b BackOff, timer Timer) *Ticker {
     36 	if timer == nil {
     37 		timer = &defaultTimer{}
     38 	}
     39 	c := make(chan time.Time)
     40 	t := &Ticker{
     41 		C:     c,
     42 		c:     c,
     43 		b:     b,
     44 		ctx:   getContext(b),
     45 		timer: timer,
     46 		stop:  make(chan struct{}),
     47 	}
     48 	t.b.Reset()
     49 	go t.run()
     50 	return t
     51 }
     52 
     53 // Stop turns off a ticker. After Stop, no more ticks will be sent.
     54 func (t *Ticker) Stop() {
     55 	t.stopOnce.Do(func() { close(t.stop) })
     56 }
     57 
     58 func (t *Ticker) run() {
     59 	c := t.c
     60 	defer close(c)
     61 
     62 	// Ticker is guaranteed to tick at least once.
     63 	afterC := t.send(time.Now())
     64 
     65 	for {
     66 		if afterC == nil {
     67 			return
     68 		}
     69 
     70 		select {
     71 		case tick := <-afterC:
     72 			afterC = t.send(tick)
     73 		case <-t.stop:
     74 			t.c = nil // Prevent future ticks from being sent to the channel.
     75 			return
     76 		case <-t.ctx.Done():
     77 			return
     78 		}
     79 	}
     80 }
     81 
     82 func (t *Ticker) send(tick time.Time) <-chan time.Time {
     83 	select {
     84 	case t.c <- tick:
     85 	case <-t.stop:
     86 		return nil
     87 	}
     88 
     89 	next := t.b.NextBackOff()
     90 	if next == Stop {
     91 		t.Stop()
     92 		return nil
     93 	}
     94 
     95 	t.timer.Start(next)
     96 	return t.timer.C()
     97 }