github.com/searKing/golang/go@v1.2.117/time/wait.go (about) 1 // Copyright 2020 The searKing Author. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package time 6 7 import ( 8 "context" 9 "time" 10 11 "github.com/searKing/golang/go/runtime" 12 ) 13 14 // Forever calls f every period for ever. 15 // 16 // Forever is syntactic sugar on top of Forever, without resetCh. 17 // Example: time.Second 18 // 2021/04/09 12:45:08 Apr 9 12:45:08 19 // 2021/04/09 12:45:09 Apr 9 12:45:09 20 // 2021/04/09 12:45:10 Apr 9 12:45:10 21 // 2021/04/09 12:45:11 Apr 9 12:45:11 22 // 2021/04/09 12:45:12 Apr 9 12:45:12 23 // 2021/04/09 12:45:13 Apr 9 12:45:13 24 // 2021/04/09 12:45:14 Apr 9 12:45:14 25 func Forever(f func(), period time.Duration) { 26 Until(context.Background(), func(ctx context.Context) { f() }, period) 27 } 28 29 // ForeverWithReset calls f every period for ever. 30 // 31 // ForeverWithReset is syntactic sugar on top of UntilWithReset. 32 // Example: time.Second 33 // 2021/04/09 12:45:08 Apr 9 12:45:08 34 // 2021/04/09 12:45:09 Apr 9 12:45:09 35 // 2021/04/09 12:45:10 Apr 9 12:45:10 36 // 2021/04/09 12:45:11 Apr 9 12:45:11 37 // 2021/04/09 12:45:12 Apr 9 12:45:12 38 // 2021/04/09 12:45:13 Apr 9 12:45:13 39 // 2021/04/09 12:45:14 Apr 9 12:45:14 40 func ForeverWithReset(f func(), resetCh chan struct{}, period time.Duration) { 41 UntilWithReset(context.Background(), func(ctx context.Context) { f() }, resetCh, period) 42 } 43 44 // Until loops until context is done, running f every period. 45 // 46 // Until is syntactic sugar on top of UntilWithReset, without resetCh. 47 func Until(ctx context.Context, f func(ctx context.Context), period time.Duration) { 48 UntilWithReset(ctx, f, nil, period) 49 } 50 51 // UntilWithReset loops until context is done, running f every period. 52 // 53 // UntilWithReset is syntactic sugar on top of JitterUntilWithReset with zero jitter factor and 54 // with sliding = true (which means the timer for period starts after the f 55 // completes). 56 // Example: time.Second for period and sleep in f 57 // 2021/04/09 12:48:03 Apr 9 12:48:03 58 // 2021/04/09 12:48:05 Apr 9 12:48:05 59 // 2021/04/09 12:48:07 Apr 9 12:48:07 60 // 2021/04/09 12:48:09 Apr 9 12:48:09 61 // 2021/04/09 12:48:11 Apr 9 12:48:11 62 // 2021/04/09 12:48:13 Apr 9 12:48:13 63 func UntilWithReset(ctx context.Context, f func(ctx context.Context), resetCh chan struct{}, period time.Duration) { 64 JitterUntilWithReset(ctx, f, resetCh, true, 65 WithExponentialBackOffOptionRandomizationFactor(0), 66 WithExponentialBackOffOptionMultiplier(1), 67 WithExponentialBackOffOptionInitialInterval(period), 68 WithExponentialBackOffOptionMaxElapsedDuration(-1)) 69 } 70 71 // NonSlidingUntil loops until context is done, running f every 72 // period. 73 // 74 // NonSlidingUntil is syntactic sugar on top of NonSlidingUntilWithReset, without resetCh. 75 func NonSlidingUntil(ctx context.Context, f func(ctx context.Context), period time.Duration) { 76 NonSlidingUntilWithReset(ctx, f, nil, period) 77 } 78 79 // NonSlidingUntilWithReset loops until context is done, running f every 80 // period. 81 // 82 // NonSlidingUntilWithReset is syntactic sugar on top of JitterUntilWithReset with zero jitter 83 // factor, with sliding = false (meaning the timer for period starts at the same 84 // time as the function starts). 85 // Example: time.Second for period and sleep in f 86 // 2021/04/09 12:45:08 Apr 9 12:45:08 87 // 2021/04/09 12:45:09 Apr 9 12:45:09 88 // 2021/04/09 12:45:10 Apr 9 12:45:10 89 // 2021/04/09 12:45:11 Apr 9 12:45:11 90 // 2021/04/09 12:45:12 Apr 9 12:45:12 91 // 2021/04/09 12:45:13 Apr 9 12:45:13 92 // 2021/04/09 12:45:14 Apr 9 12:45:14 93 func NonSlidingUntilWithReset(ctx context.Context, f func(ctx context.Context), resetCh chan struct{}, period time.Duration) { 94 JitterUntilWithReset(ctx, f, resetCh, false, 95 WithExponentialBackOffOptionRandomizationFactor(0), 96 WithExponentialBackOffOptionMultiplier(1), 97 WithExponentialBackOffOptionInitialInterval(period), 98 WithExponentialBackOffOptionMaxElapsedDuration(-1)) 99 } 100 101 // JitterUntil loops until context is done, running f every period. 102 // JitterUntil is syntactic sugar on top of JitterUntilWithReset, without resetCh. 103 func JitterUntil(ctx context.Context, f func(ctx context.Context), sliding bool, opts ...ExponentialBackOffOption) { 104 JitterUntilWithReset(ctx, f, nil, sliding, opts...) 105 } 106 107 // JitterUntilWithReset loops until context is done, running f every period. 108 // 109 // period set by WithExponentialBackOffOptionInitialInterval 110 // jitterFactor set by WithExponentialBackOffOptionRandomizationFactor 111 // If jitterFactor is positive, the period is jittered before every run of f. 112 // If jitterFactor is not positive, the period is unchanged and not jittered. 113 // 114 // If sliding is true, the period is computed after f runs. If it is false then 115 // period includes the runtime for f. 116 // backoff is reset if resetCh has data 117 // 118 // Cancel context to stop. f may not be invoked if context is already expired. 119 func JitterUntilWithReset(ctx context.Context, f func(ctx context.Context), resetCh chan struct{}, sliding bool, opts ...ExponentialBackOffOption) { 120 BackoffUntilWithReset(ctx, f, resetCh, NewExponentialBackOff(opts...), sliding) 121 } 122 123 // BackoffUntil loops until context is done, run f every duration given by BackoffManager. 124 // BackoffUntil is syntactic sugar on top of BackoffUntilWithReset, without resetCh. 125 func BackoffUntil(ctx context.Context, f func(ctx context.Context), backoff BackOff, sliding bool) { 126 BackoffUntilWithReset(ctx, f, nil, backoff, sliding) 127 } 128 129 // BackoffUntilWithReset loops until context is done, run f every duration given by BackoffManager. 130 // 131 // If sliding is true, the period is computed after f runs. If it is false then 132 // period includes the runtime for f. 133 // backoff is reset if resetCh has data 134 func BackoffUntilWithReset(ctx context.Context, 135 f func(ctx context.Context), resetCh chan struct{}, backoff BackOff, sliding bool) { 136 var elapsed time.Duration 137 var ok bool 138 139 var drainResetCh = func() { 140 // To ensure the channel is empty, check the 141 // return value and drain the channel. 142 for { 143 select { 144 case <-resetCh: 145 default: 146 return 147 } 148 } 149 } 150 for { 151 select { 152 case <-ctx.Done(): 153 return 154 case <-resetCh: 155 backoff.Reset() 156 drainResetCh() 157 default: 158 } 159 160 var cost Cost 161 if !sliding { 162 cost.Start() 163 elapsed, ok = backoff.NextBackOff() 164 } 165 166 func() { 167 defer runtime.DefaultPanic.Recover() 168 f(ctx) 169 }() 170 if !sliding { 171 elapsed -= cost.Elapse() 172 } 173 174 if sliding { 175 elapsed, ok = backoff.NextBackOff() 176 } 177 if !ok { 178 return 179 } 180 181 func() { 182 if elapsed <= 0 { 183 return 184 } 185 timer := time.NewTimer(elapsed) 186 defer timer.Stop() 187 188 // NOTE: b/c there is no priority selection in golang 189 // it is possible for this to race, meaning we could 190 // trigger t.C and stopCh, and t.C select falls through. 191 // In order to mitigate we re-check stopCh at the beginning 192 // of every loop to prevent extra executions of f(). 193 select { 194 case <-ctx.Done(): 195 return 196 case <-timer.C: 197 case <-resetCh: 198 backoff.Reset() 199 drainResetCh() 200 } 201 }() 202 } 203 }