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  }