github.com/git-amp/amp-sdk-go@v0.7.5/stdlib/utils/time.go (about) 1 package utils 2 3 import ( 4 "time" 5 ) 6 7 type ExponentialBackoff struct { 8 Min time.Duration 9 Max time.Duration 10 current time.Duration 11 previousIncr time.Time 12 } 13 14 func (eb *ExponentialBackoff) Ready() (ready bool, until time.Duration) { 15 if eb.previousIncr.IsZero() { 16 return true, 0 17 } 18 whenReady := eb.previousIncr.Add(eb.current) 19 ready = time.Now().After(whenReady) 20 if !ready { 21 until = whenReady.Sub(time.Now()) 22 } 23 return 24 } 25 26 func (eb *ExponentialBackoff) Next() time.Duration { 27 if eb.current == 0 { 28 eb.current = eb.Min 29 } 30 current := eb.current 31 eb.current *= 2 32 if eb.current > eb.Max { 33 eb.current = eb.Max 34 } 35 eb.previousIncr = time.Now() 36 return current 37 } 38 39 func (eb *ExponentialBackoff) Wait() { 40 time.Sleep(eb.Next()) 41 } 42 43 func (eb *ExponentialBackoff) Reset() { 44 eb.current = eb.Min 45 } 46 47 type Ticker interface { 48 Start() 49 Close() 50 Notify() <-chan time.Time 51 } 52 53 type StaticTicker struct { 54 t *time.Ticker 55 interval time.Duration 56 } 57 58 func NewStaticTicker(interval time.Duration) StaticTicker { 59 return StaticTicker{ 60 t: time.NewTicker(interval), 61 interval: interval, 62 } 63 } 64 65 func (t StaticTicker) Start() { 66 t.t.Reset(t.interval) 67 } 68 69 func (t StaticTicker) Close() { 70 t.t.Stop() 71 } 72 73 func (t StaticTicker) Notify() <-chan time.Time { 74 return t.t.C 75 } 76 77 type ExponentialBackoffTicker struct { 78 backoff ExponentialBackoff 79 chTick chan time.Time 80 chReset chan struct{} 81 chStop chan struct{} 82 chDone chan struct{} 83 } 84 85 func NewExponentialBackoffTicker(min, max time.Duration) *ExponentialBackoffTicker { 86 return &ExponentialBackoffTicker{ 87 backoff: ExponentialBackoff{Min: min, Max: max}, 88 chTick: make(chan time.Time), 89 chReset: make(chan struct{}), 90 chStop: make(chan struct{}), 91 chDone: make(chan struct{}), 92 } 93 } 94 95 func (t *ExponentialBackoffTicker) Start() { 96 go func() { 97 defer close(t.chDone) 98 for { 99 func() { 100 duration := t.backoff.Next() 101 timer := time.NewTimer(duration) 102 defer timer.Stop() 103 104 select { 105 case <-t.chStop: 106 return 107 108 case <-t.chReset: 109 t.backoff.Reset() 110 111 case now := <-timer.C: 112 select { 113 case <-t.chStop: 114 return 115 case t.chTick <- now: 116 } 117 } 118 }() 119 } 120 }() 121 } 122 123 func (t *ExponentialBackoffTicker) Reset() { 124 select { 125 case <-t.chStop: 126 case t.chReset <- struct{}{}: 127 } 128 } 129 130 func (t *ExponentialBackoffTicker) Close() { 131 close(t.chStop) 132 <-t.chDone 133 } 134 135 func (t *ExponentialBackoffTicker) Notify() <-chan time.Time { 136 return t.chTick 137 }