github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/util/wait/wait.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package wait
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"math"
    23  	"math/rand"
    24  	"sync"
    25  	"time"
    26  
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/runtime"
    28  	"k8s.io/utils/clock"
    29  )
    30  
    31  // For any test of the style:
    32  //
    33  //	...
    34  //	<- time.After(timeout):
    35  //	   t.Errorf("Timed out")
    36  //
    37  // The value for timeout should effectively be "forever." Obviously we don't want our tests to truly lock up forever, but 30s
    38  // is long enough that it is effectively forever for the things that can slow down a run on a heavily contended machine
    39  // (GC, seeks, etc), but not so long as to make a developer ctrl-c a test run if they do happen to break that test.
    40  var ForeverTestTimeout = time.Second * 30
    41  
    42  // NeverStop may be passed to Until to make it never stop.
    43  var NeverStop <-chan struct{} = make(chan struct{})
    44  
    45  // Group allows to start a group of goroutines and wait for their completion.
    46  type Group struct {
    47  	wg sync.WaitGroup
    48  }
    49  
    50  func (g *Group) Wait() {
    51  	g.wg.Wait()
    52  }
    53  
    54  // StartWithChannel starts f in a new goroutine in the group.
    55  // stopCh is passed to f as an argument. f should stop when stopCh is available.
    56  func (g *Group) StartWithChannel(stopCh <-chan struct{}, f func(stopCh <-chan struct{})) {
    57  	g.Start(func() {
    58  		f(stopCh)
    59  	})
    60  }
    61  
    62  // StartWithContext starts f in a new goroutine in the group.
    63  // ctx is passed to f as an argument. f should stop when ctx.Done() is available.
    64  func (g *Group) StartWithContext(ctx context.Context, f func(context.Context)) {
    65  	g.Start(func() {
    66  		f(ctx)
    67  	})
    68  }
    69  
    70  // Start starts f in a new goroutine in the group.
    71  func (g *Group) Start(f func()) {
    72  	g.wg.Add(1)
    73  	go func() {
    74  		defer g.wg.Done()
    75  		f()
    76  	}()
    77  }
    78  
    79  // Forever calls f every period for ever.
    80  //
    81  // Forever is syntactic sugar on top of Until.
    82  func Forever(f func(), period time.Duration) {
    83  	Until(f, period, NeverStop)
    84  }
    85  
    86  // Until loops until stop channel is closed, running f every period.
    87  //
    88  // Until is syntactic sugar on top of JitterUntil with zero jitter factor and
    89  // with sliding = true (which means the timer for period starts after the f
    90  // completes).
    91  func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
    92  	JitterUntil(f, period, 0.0, true, stopCh)
    93  }
    94  
    95  // UntilWithContext loops until context is done, running f every period.
    96  //
    97  // UntilWithContext is syntactic sugar on top of JitterUntilWithContext
    98  // with zero jitter factor and with sliding = true (which means the timer
    99  // for period starts after the f completes).
   100  func UntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) {
   101  	JitterUntilWithContext(ctx, f, period, 0.0, true)
   102  }
   103  
   104  // NonSlidingUntil loops until stop channel is closed, running f every
   105  // period.
   106  //
   107  // NonSlidingUntil is syntactic sugar on top of JitterUntil with zero jitter
   108  // factor, with sliding = false (meaning the timer for period starts at the same
   109  // time as the function starts).
   110  func NonSlidingUntil(f func(), period time.Duration, stopCh <-chan struct{}) {
   111  	JitterUntil(f, period, 0.0, false, stopCh)
   112  }
   113  
   114  // NonSlidingUntilWithContext loops until context is done, running f every
   115  // period.
   116  //
   117  // NonSlidingUntilWithContext is syntactic sugar on top of JitterUntilWithContext
   118  // with zero jitter factor, with sliding = false (meaning the timer for period
   119  // starts at the same time as the function starts).
   120  func NonSlidingUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration) {
   121  	JitterUntilWithContext(ctx, f, period, 0.0, false)
   122  }
   123  
   124  // JitterUntil loops until stop channel is closed, running f every period.
   125  //
   126  // If jitterFactor is positive, the period is jittered before every run of f.
   127  // If jitterFactor is not positive, the period is unchanged and not jittered.
   128  //
   129  // If sliding is true, the period is computed after f runs. If it is false then
   130  // period includes the runtime for f.
   131  //
   132  // Close stopCh to stop. f may not be invoked if stop channel is already
   133  // closed. Pass NeverStop to if you don't want it stop.
   134  func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) {
   135  	BackoffUntil(f, NewJitteredBackoffManager(period, jitterFactor, &clock.RealClock{}), sliding, stopCh)
   136  }
   137  
   138  // BackoffUntil loops until stop channel is closed, run f every duration given by BackoffManager.
   139  //
   140  // If sliding is true, the period is computed after f runs. If it is false then
   141  // period includes the runtime for f.
   142  func BackoffUntil(f func(), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) {
   143  	var t clock.Timer
   144  	for {
   145  		select {
   146  		case <-stopCh:
   147  			return
   148  		default:
   149  		}
   150  
   151  		if !sliding {
   152  			t = backoff.Backoff()
   153  		}
   154  
   155  		func() {
   156  			defer runtime.HandleCrash()
   157  			f()
   158  		}()
   159  
   160  		if sliding {
   161  			t = backoff.Backoff()
   162  		}
   163  
   164  		// NOTE: b/c there is no priority selection in golang
   165  		// it is possible for this to race, meaning we could
   166  		// trigger t.C and stopCh, and t.C select falls through.
   167  		// In order to mitigate we re-check stopCh at the beginning
   168  		// of every loop to prevent extra executions of f().
   169  		select {
   170  		case <-stopCh:
   171  			if !t.Stop() {
   172  				<-t.C()
   173  			}
   174  			return
   175  		case <-t.C():
   176  		}
   177  	}
   178  }
   179  
   180  // JitterUntilWithContext loops until context is done, running f every period.
   181  //
   182  // If jitterFactor is positive, the period is jittered before every run of f.
   183  // If jitterFactor is not positive, the period is unchanged and not jittered.
   184  //
   185  // If sliding is true, the period is computed after f runs. If it is false then
   186  // period includes the runtime for f.
   187  //
   188  // Cancel context to stop. f may not be invoked if context is already expired.
   189  func JitterUntilWithContext(ctx context.Context, f func(context.Context), period time.Duration, jitterFactor float64, sliding bool) {
   190  	JitterUntil(func() { f(ctx) }, period, jitterFactor, sliding, ctx.Done())
   191  }
   192  
   193  // Jitter returns a time.Duration between duration and duration + maxFactor *
   194  // duration.
   195  //
   196  // This allows clients to avoid converging on periodic behavior. If maxFactor
   197  // is 0.0, a suggested default value will be chosen.
   198  func Jitter(duration time.Duration, maxFactor float64) time.Duration {
   199  	if maxFactor <= 0.0 {
   200  		maxFactor = 1.0
   201  	}
   202  	wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
   203  	return wait
   204  }
   205  
   206  // ErrWaitTimeout is returned when the condition exited without success.
   207  var ErrWaitTimeout = errors.New("timed out waiting for the condition")
   208  
   209  // ConditionFunc returns true if the condition is satisfied, or an error
   210  // if the loop should be aborted.
   211  type ConditionFunc func() (done bool, err error)
   212  
   213  // ConditionWithContextFunc returns true if the condition is satisfied, or an error
   214  // if the loop should be aborted.
   215  //
   216  // The caller passes along a context that can be used by the condition function.
   217  type ConditionWithContextFunc func(context.Context) (done bool, err error)
   218  
   219  // WithContext converts a ConditionFunc into a ConditionWithContextFunc
   220  func (cf ConditionFunc) WithContext() ConditionWithContextFunc {
   221  	return func(context.Context) (done bool, err error) {
   222  		return cf()
   223  	}
   224  }
   225  
   226  // runConditionWithCrashProtection runs a ConditionFunc with crash protection
   227  func runConditionWithCrashProtection(condition ConditionFunc) (bool, error) {
   228  	return runConditionWithCrashProtectionWithContext(context.TODO(), condition.WithContext())
   229  }
   230  
   231  // runConditionWithCrashProtectionWithContext runs a
   232  // ConditionWithContextFunc with crash protection.
   233  func runConditionWithCrashProtectionWithContext(ctx context.Context, condition ConditionWithContextFunc) (bool, error) {
   234  	defer runtime.HandleCrash()
   235  	return condition(ctx)
   236  }
   237  
   238  // Backoff holds parameters applied to a Backoff function.
   239  type Backoff struct {
   240  	// The initial duration.
   241  	Duration time.Duration
   242  	// Duration is multiplied by factor each iteration, if factor is not zero
   243  	// and the limits imposed by Steps and Cap have not been reached.
   244  	// Should not be negative.
   245  	// The jitter does not contribute to the updates to the duration parameter.
   246  	Factor float64
   247  	// The sleep at each iteration is the duration plus an additional
   248  	// amount chosen uniformly at random from the interval between
   249  	// zero and `jitter*duration`.
   250  	Jitter float64
   251  	// The remaining number of iterations in which the duration
   252  	// parameter may change (but progress can be stopped earlier by
   253  	// hitting the cap). If not positive, the duration is not
   254  	// changed. Used for exponential backoff in combination with
   255  	// Factor and Cap.
   256  	Steps int
   257  	// A limit on revised values of the duration parameter. If a
   258  	// multiplication by the factor parameter would make the duration
   259  	// exceed the cap then the duration is set to the cap and the
   260  	// steps parameter is set to zero.
   261  	Cap time.Duration
   262  }
   263  
   264  // Step (1) returns an amount of time to sleep determined by the
   265  // original Duration and Jitter and (2) mutates the provided Backoff
   266  // to update its Steps and Duration.
   267  func (b *Backoff) Step() time.Duration {
   268  	if b.Steps < 1 {
   269  		if b.Jitter > 0 {
   270  			return Jitter(b.Duration, b.Jitter)
   271  		}
   272  		return b.Duration
   273  	}
   274  	b.Steps--
   275  
   276  	duration := b.Duration
   277  
   278  	// calculate the next step
   279  	if b.Factor != 0 {
   280  		b.Duration = time.Duration(float64(b.Duration) * b.Factor)
   281  		if b.Cap > 0 && b.Duration > b.Cap {
   282  			b.Duration = b.Cap
   283  			b.Steps = 0
   284  		}
   285  	}
   286  
   287  	if b.Jitter > 0 {
   288  		duration = Jitter(duration, b.Jitter)
   289  	}
   290  	return duration
   291  }
   292  
   293  // ContextForChannel derives a child context from a parent channel.
   294  //
   295  // The derived context's Done channel is closed when the returned cancel function
   296  // is called or when the parent channel is closed, whichever happens first.
   297  //
   298  // Note the caller must *always* call the CancelFunc, otherwise resources may be leaked.
   299  func ContextForChannel(parentCh <-chan struct{}) (context.Context, context.CancelFunc) {
   300  	ctx, cancel := context.WithCancel(context.Background())
   301  
   302  	go func() {
   303  		select {
   304  		case <-parentCh:
   305  			cancel()
   306  		case <-ctx.Done():
   307  		}
   308  	}()
   309  	return ctx, cancel
   310  }
   311  
   312  // BackoffManager manages backoff with a particular scheme based on its underlying implementation. It provides
   313  // an interface to return a timer for backoff, and caller shall backoff until Timer.C() drains. If the second Backoff()
   314  // is called before the timer from the first Backoff() call finishes, the first timer will NOT be drained and result in
   315  // undetermined behavior.
   316  // The BackoffManager is supposed to be called in a single-threaded environment.
   317  type BackoffManager interface {
   318  	Backoff() clock.Timer
   319  }
   320  
   321  type exponentialBackoffManagerImpl struct {
   322  	backoff              *Backoff
   323  	backoffTimer         clock.Timer
   324  	lastBackoffStart     time.Time
   325  	initialBackoff       time.Duration
   326  	backoffResetDuration time.Duration
   327  	clock                clock.Clock
   328  }
   329  
   330  // NewExponentialBackoffManager returns a manager for managing exponential backoff. Each backoff is jittered and
   331  // backoff will not exceed the given max. If the backoff is not called within resetDuration, the backoff is reset.
   332  // This backoff manager is used to reduce load during upstream unhealthiness.
   333  func NewExponentialBackoffManager(initBackoff, maxBackoff, resetDuration time.Duration, backoffFactor, jitter float64, c clock.Clock) BackoffManager {
   334  	return &exponentialBackoffManagerImpl{
   335  		backoff: &Backoff{
   336  			Duration: initBackoff,
   337  			Factor:   backoffFactor,
   338  			Jitter:   jitter,
   339  
   340  			// the current impl of wait.Backoff returns Backoff.Duration once steps are used up, which is not
   341  			// what we ideally need here, we set it to max int and assume we will never use up the steps
   342  			Steps: math.MaxInt32,
   343  			Cap:   maxBackoff,
   344  		},
   345  		backoffTimer:         nil,
   346  		initialBackoff:       initBackoff,
   347  		lastBackoffStart:     c.Now(),
   348  		backoffResetDuration: resetDuration,
   349  		clock:                c,
   350  	}
   351  }
   352  
   353  func (b *exponentialBackoffManagerImpl) getNextBackoff() time.Duration {
   354  	if b.clock.Now().Sub(b.lastBackoffStart) > b.backoffResetDuration {
   355  		b.backoff.Steps = math.MaxInt32
   356  		b.backoff.Duration = b.initialBackoff
   357  	}
   358  	b.lastBackoffStart = b.clock.Now()
   359  	return b.backoff.Step()
   360  }
   361  
   362  // Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for exponential backoff.
   363  // The returned timer must be drained before calling Backoff() the second time
   364  func (b *exponentialBackoffManagerImpl) Backoff() clock.Timer {
   365  	if b.backoffTimer == nil {
   366  		b.backoffTimer = b.clock.NewTimer(b.getNextBackoff())
   367  	} else {
   368  		b.backoffTimer.Reset(b.getNextBackoff())
   369  	}
   370  	return b.backoffTimer
   371  }
   372  
   373  type jitteredBackoffManagerImpl struct {
   374  	clock        clock.Clock
   375  	duration     time.Duration
   376  	jitter       float64
   377  	backoffTimer clock.Timer
   378  }
   379  
   380  // NewJitteredBackoffManager returns a BackoffManager that backoffs with given duration plus given jitter. If the jitter
   381  // is negative, backoff will not be jittered.
   382  func NewJitteredBackoffManager(duration time.Duration, jitter float64, c clock.Clock) BackoffManager {
   383  	return &jitteredBackoffManagerImpl{
   384  		clock:        c,
   385  		duration:     duration,
   386  		jitter:       jitter,
   387  		backoffTimer: nil,
   388  	}
   389  }
   390  
   391  func (j *jitteredBackoffManagerImpl) getNextBackoff() time.Duration {
   392  	jitteredPeriod := j.duration
   393  	if j.jitter > 0.0 {
   394  		jitteredPeriod = Jitter(j.duration, j.jitter)
   395  	}
   396  	return jitteredPeriod
   397  }
   398  
   399  // Backoff implements BackoffManager.Backoff, it returns a timer so caller can block on the timer for jittered backoff.
   400  // The returned timer must be drained before calling Backoff() the second time
   401  func (j *jitteredBackoffManagerImpl) Backoff() clock.Timer {
   402  	backoff := j.getNextBackoff()
   403  	if j.backoffTimer == nil {
   404  		j.backoffTimer = j.clock.NewTimer(backoff)
   405  	} else {
   406  		j.backoffTimer.Reset(backoff)
   407  	}
   408  	return j.backoffTimer
   409  }
   410  
   411  // ExponentialBackoff repeats a condition check with exponential backoff.
   412  //
   413  // It repeatedly checks the condition and then sleeps, using `backoff.Step()`
   414  // to determine the length of the sleep and adjust Duration and Steps.
   415  // Stops and returns as soon as:
   416  // 1. the condition check returns true or an error,
   417  // 2. `backoff.Steps` checks of the condition have been done, or
   418  // 3. a sleep truncated by the cap on duration has been completed.
   419  // In case (1) the returned error is what the condition function returned.
   420  // In all other cases, ErrWaitTimeout is returned.
   421  func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
   422  	for backoff.Steps > 0 {
   423  		if ok, err := runConditionWithCrashProtection(condition); err != nil || ok {
   424  			return err
   425  		}
   426  		if backoff.Steps == 1 {
   427  			break
   428  		}
   429  		time.Sleep(backoff.Step())
   430  	}
   431  	return ErrWaitTimeout
   432  }
   433  
   434  // Poll tries a condition func until it returns true, an error, or the timeout
   435  // is reached.
   436  //
   437  // Poll always waits the interval before the run of 'condition'.
   438  // 'condition' will always be invoked at least once.
   439  //
   440  // Some intervals may be missed if the condition takes too long or the time
   441  // window is too short.
   442  //
   443  // If you want to Poll something forever, see PollInfinite.
   444  func Poll(interval, timeout time.Duration, condition ConditionFunc) error {
   445  	return PollWithContext(context.Background(), interval, timeout, condition.WithContext())
   446  }
   447  
   448  // PollWithContext tries a condition func until it returns true, an error,
   449  // or when the context expires or the timeout is reached, whichever
   450  // happens first.
   451  //
   452  // PollWithContext always waits the interval before the run of 'condition'.
   453  // 'condition' will always be invoked at least once.
   454  //
   455  // Some intervals may be missed if the condition takes too long or the time
   456  // window is too short.
   457  //
   458  // If you want to Poll something forever, see PollInfinite.
   459  func PollWithContext(ctx context.Context, interval, timeout time.Duration, condition ConditionWithContextFunc) error {
   460  	return poll(ctx, false, poller(interval, timeout), condition)
   461  }
   462  
   463  // PollUntil tries a condition func until it returns true, an error or stopCh is
   464  // closed.
   465  //
   466  // PollUntil always waits interval before the first run of 'condition'.
   467  // 'condition' will always be invoked at least once.
   468  func PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
   469  	ctx, cancel := ContextForChannel(stopCh)
   470  	defer cancel()
   471  	return PollUntilWithContext(ctx, interval, condition.WithContext())
   472  }
   473  
   474  // PollUntilWithContext tries a condition func until it returns true,
   475  // an error or the specified context is cancelled or expired.
   476  //
   477  // PollUntilWithContext always waits interval before the first run of 'condition'.
   478  // 'condition' will always be invoked at least once.
   479  func PollUntilWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   480  	return poll(ctx, false, poller(interval, 0), condition)
   481  }
   482  
   483  // PollInfinite tries a condition func until it returns true or an error
   484  //
   485  // PollInfinite always waits the interval before the run of 'condition'.
   486  //
   487  // Some intervals may be missed if the condition takes too long or the time
   488  // window is too short.
   489  func PollInfinite(interval time.Duration, condition ConditionFunc) error {
   490  	return PollInfiniteWithContext(context.Background(), interval, condition.WithContext())
   491  }
   492  
   493  // PollInfiniteWithContext tries a condition func until it returns true or an error
   494  //
   495  // PollInfiniteWithContext always waits the interval before the run of 'condition'.
   496  //
   497  // Some intervals may be missed if the condition takes too long or the time
   498  // window is too short.
   499  func PollInfiniteWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   500  	return poll(ctx, false, poller(interval, 0), condition)
   501  }
   502  
   503  // PollImmediate tries a condition func until it returns true, an error, or the timeout
   504  // is reached.
   505  //
   506  // PollImmediate always checks 'condition' before waiting for the interval. 'condition'
   507  // will always be invoked at least once.
   508  //
   509  // Some intervals may be missed if the condition takes too long or the time
   510  // window is too short.
   511  //
   512  // If you want to immediately Poll something forever, see PollImmediateInfinite.
   513  func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error {
   514  	return PollImmediateWithContext(context.Background(), interval, timeout, condition.WithContext())
   515  }
   516  
   517  // PollImmediateWithContext tries a condition func until it returns true, an error,
   518  // or the timeout is reached or the specified context expires, whichever happens first.
   519  //
   520  // PollImmediateWithContext always checks 'condition' before waiting for the interval.
   521  // 'condition' will always be invoked at least once.
   522  //
   523  // Some intervals may be missed if the condition takes too long or the time
   524  // window is too short.
   525  //
   526  // If you want to immediately Poll something forever, see PollImmediateInfinite.
   527  func PollImmediateWithContext(ctx context.Context, interval, timeout time.Duration, condition ConditionWithContextFunc) error {
   528  	return poll(ctx, true, poller(interval, timeout), condition)
   529  }
   530  
   531  // PollImmediateUntil tries a condition func until it returns true, an error or stopCh is closed.
   532  //
   533  // PollImmediateUntil runs the 'condition' before waiting for the interval.
   534  // 'condition' will always be invoked at least once.
   535  func PollImmediateUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
   536  	ctx, cancel := ContextForChannel(stopCh)
   537  	defer cancel()
   538  	return PollImmediateUntilWithContext(ctx, interval, condition.WithContext())
   539  }
   540  
   541  // PollImmediateUntilWithContext tries a condition func until it returns true,
   542  // an error or the specified context is cancelled or expired.
   543  //
   544  // PollImmediateUntilWithContext runs the 'condition' before waiting for the interval.
   545  // 'condition' will always be invoked at least once.
   546  func PollImmediateUntilWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   547  	return poll(ctx, true, poller(interval, 0), condition)
   548  }
   549  
   550  // PollImmediateInfinite tries a condition func until it returns true or an error
   551  //
   552  // PollImmediateInfinite runs the 'condition' before waiting for the interval.
   553  //
   554  // Some intervals may be missed if the condition takes too long or the time
   555  // window is too short.
   556  func PollImmediateInfinite(interval time.Duration, condition ConditionFunc) error {
   557  	return PollImmediateInfiniteWithContext(context.Background(), interval, condition.WithContext())
   558  }
   559  
   560  // PollImmediateInfiniteWithContext tries a condition func until it returns true
   561  // or an error or the specified context gets cancelled or expired.
   562  //
   563  // PollImmediateInfiniteWithContext runs the 'condition' before waiting for the interval.
   564  //
   565  // Some intervals may be missed if the condition takes too long or the time
   566  // window is too short.
   567  func PollImmediateInfiniteWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   568  	return poll(ctx, true, poller(interval, 0), condition)
   569  }
   570  
   571  // Internally used, each of the public 'Poll*' function defined in this
   572  // package should invoke this internal function with appropriate parameters.
   573  // ctx: the context specified by the caller, for infinite polling pass
   574  // a context that never gets cancelled or expired.
   575  // immediate: if true, the 'condition' will be invoked before waiting for the interval,
   576  // in this case 'condition' will always be invoked at least once.
   577  // wait: user specified WaitFunc function that controls at what interval the condition
   578  // function should be invoked periodically and whether it is bound by a timeout.
   579  // condition: user specified ConditionWithContextFunc function.
   580  func poll(ctx context.Context, immediate bool, wait WaitWithContextFunc, condition ConditionWithContextFunc) error {
   581  	if immediate {
   582  		done, err := runConditionWithCrashProtectionWithContext(ctx, condition)
   583  		if err != nil {
   584  			return err
   585  		}
   586  		if done {
   587  			return nil
   588  		}
   589  	}
   590  
   591  	select {
   592  	case <-ctx.Done():
   593  		// returning ctx.Err() will break backward compatibility
   594  		return ErrWaitTimeout
   595  	default:
   596  		return WaitForWithContext(ctx, wait, condition)
   597  	}
   598  }
   599  
   600  // WaitFunc creates a channel that receives an item every time a test
   601  // should be executed and is closed when the last test should be invoked.
   602  type WaitFunc func(done <-chan struct{}) <-chan struct{}
   603  
   604  // WithContext converts the WaitFunc to an equivalent WaitWithContextFunc
   605  func (w WaitFunc) WithContext() WaitWithContextFunc {
   606  	return func(ctx context.Context) <-chan struct{} {
   607  		return w(ctx.Done())
   608  	}
   609  }
   610  
   611  // WaitWithContextFunc creates a channel that receives an item every time a test
   612  // should be executed and is closed when the last test should be invoked.
   613  //
   614  // When the specified context gets cancelled or expires the function
   615  // stops sending item and returns immediately.
   616  type WaitWithContextFunc func(ctx context.Context) <-chan struct{}
   617  
   618  // WaitFor continually checks 'fn' as driven by 'wait'.
   619  //
   620  // WaitFor gets a channel from 'wait()”, and then invokes 'fn' once for every value
   621  // placed on the channel and once more when the channel is closed. If the channel is closed
   622  // and 'fn' returns false without error, WaitFor returns ErrWaitTimeout.
   623  //
   624  // If 'fn' returns an error the loop ends and that error is returned. If
   625  // 'fn' returns true the loop ends and nil is returned.
   626  //
   627  // ErrWaitTimeout will be returned if the 'done' channel is closed without fn ever
   628  // returning true.
   629  //
   630  // When the done channel is closed, because the golang `select` statement is
   631  // "uniform pseudo-random", the `fn` might still run one or multiple time,
   632  // though eventually `WaitFor` will return.
   633  func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error {
   634  	ctx, cancel := ContextForChannel(done)
   635  	defer cancel()
   636  	return WaitForWithContext(ctx, wait.WithContext(), fn.WithContext())
   637  }
   638  
   639  // WaitForWithContext continually checks 'fn' as driven by 'wait'.
   640  //
   641  // WaitForWithContext gets a channel from 'wait()”, and then invokes 'fn'
   642  // once for every value placed on the channel and once more when the
   643  // channel is closed. If the channel is closed and 'fn'
   644  // returns false without error, WaitForWithContext returns ErrWaitTimeout.
   645  //
   646  // If 'fn' returns an error the loop ends and that error is returned. If
   647  // 'fn' returns true the loop ends and nil is returned.
   648  //
   649  // context.Canceled will be returned if the ctx.Done() channel is closed
   650  // without fn ever returning true.
   651  //
   652  // When the ctx.Done() channel is closed, because the golang `select` statement is
   653  // "uniform pseudo-random", the `fn` might still run one or multiple times,
   654  // though eventually `WaitForWithContext` will return.
   655  func WaitForWithContext(ctx context.Context, wait WaitWithContextFunc, fn ConditionWithContextFunc) error {
   656  	waitCtx, cancel := context.WithCancel(context.Background())
   657  	defer cancel()
   658  	c := wait(waitCtx)
   659  	for {
   660  		select {
   661  		case _, open := <-c:
   662  			ok, err := runConditionWithCrashProtectionWithContext(ctx, fn)
   663  			if err != nil {
   664  				return err
   665  			}
   666  			if ok {
   667  				return nil
   668  			}
   669  			if !open {
   670  				return ErrWaitTimeout
   671  			}
   672  		case <-ctx.Done():
   673  			// returning ctx.Err() will break backward compatibility
   674  			return ErrWaitTimeout
   675  		}
   676  	}
   677  }
   678  
   679  // poller returns a WaitFunc that will send to the channel every interval until
   680  // timeout has elapsed and then closes the channel.
   681  //
   682  // Over very short intervals you may receive no ticks before the channel is
   683  // closed. A timeout of 0 is interpreted as an infinity, and in such a case
   684  // it would be the caller's responsibility to close the done channel.
   685  // Failure to do so would result in a leaked goroutine.
   686  //
   687  // Output ticks are not buffered. If the channel is not ready to receive an
   688  // item, the tick is skipped.
   689  func poller(interval, timeout time.Duration) WaitWithContextFunc {
   690  	return WaitWithContextFunc(func(ctx context.Context) <-chan struct{} {
   691  		ch := make(chan struct{})
   692  
   693  		go func() {
   694  			defer close(ch)
   695  
   696  			tick := time.NewTicker(interval)
   697  			defer tick.Stop()
   698  
   699  			var after <-chan time.Time
   700  			if timeout != 0 {
   701  				// time.After is more convenient, but it
   702  				// potentially leaves timers around much longer
   703  				// than necessary if we exit early.
   704  				timer := time.NewTimer(timeout)
   705  				after = timer.C
   706  				defer timer.Stop()
   707  			}
   708  
   709  			for {
   710  				select {
   711  				case <-tick.C:
   712  					// If the consumer isn't ready for this signal drop it and
   713  					// check the other channels.
   714  					select {
   715  					case ch <- struct{}{}:
   716  					default:
   717  					}
   718  				case <-after:
   719  					return
   720  				case <-ctx.Done():
   721  					return
   722  				}
   723  			}
   724  		}()
   725  
   726  		return ch
   727  	})
   728  }
   729  
   730  // ExponentialBackoffWithContext works with a request context and a Backoff. It ensures that the retry wait never
   731  // exceeds the deadline specified by the request context.
   732  func ExponentialBackoffWithContext(ctx context.Context, backoff Backoff, condition ConditionFunc) error {
   733  	for backoff.Steps > 0 {
   734  		select {
   735  		case <-ctx.Done():
   736  			return ctx.Err()
   737  		default:
   738  		}
   739  
   740  		if ok, err := runConditionWithCrashProtection(condition); err != nil || ok {
   741  			return err
   742  		}
   743  
   744  		if backoff.Steps == 1 {
   745  			break
   746  		}
   747  
   748  		waitBeforeRetry := backoff.Step()
   749  		select {
   750  		case <-ctx.Done():
   751  			return ctx.Err()
   752  		case <-time.After(waitBeforeRetry):
   753  		}
   754  	}
   755  
   756  	return ErrWaitTimeout
   757  }