diff --git a/pkg/util/wait/wait.go b/pkg/util/wait/wait.go index 34d833b44d9..34ed2301da8 100644 --- a/pkg/util/wait/wait.go +++ b/pkg/util/wait/wait.go @@ -36,33 +36,42 @@ var ForeverTestTimeout = time.Second * 30 // NeverStop may be passed to Until to make it never stop. var NeverStop <-chan struct{} = make(chan struct{}) -// Forever is syntactic sugar on top of Until +// Forever calls f every period for ever. +// +// Forever is syntactic sugar on top of Until. func Forever(f func(), period time.Duration) { Until(f, period, NeverStop) } // Until loops until stop channel is closed, running f every period. -// Until is syntactic sugar on top of JitterUntil with zero jitter -// factor, with sliding = true (which means the timer for period -// starts after the f completes). +// +// Until is syntactic sugar on top of JitterUntil with zero jitter factor and +// with sliding = true (which means the timer for period starts after the f +// completes). func Until(f func(), period time.Duration, stopCh <-chan struct{}) { JitterUntil(f, period, 0.0, true, stopCh) } // NonSlidingUntil loops until stop channel is closed, running f every -// period. NonSlidingUntil is syntactic sugar on top of JitterUntil -// with zero jitter factor, with sliding = false (meaning the timer for -// period starts at the same time as the function starts). +// period. +// +// NonSlidingUntil is syntactic sugar on top of JitterUntil with zero jitter +// factor, with sliding = false (meaning the timer for period starts at the same +// time as the function starts). func NonSlidingUntil(f func(), period time.Duration, stopCh <-chan struct{}) { JitterUntil(f, period, 0.0, false, stopCh) } // JitterUntil loops until stop channel is closed, running f every period. +// // If jitterFactor is positive, the period is jittered before every run of f. -// If jitterFactor is not positive, the period is unchanged. -// Catches any panics, and keeps going. f may not be invoked if -// stop channel is already closed. Pass NeverStop to Until if you -// don't want it stop. +// If jitterFactor is not positive, the period is unchanged and not jitterd. +// +// If slidingis true, the period is computed after f runs. If it is false then +// period includes the runtime for f. +// +// Close stopCh to stop. f may not be invoked if stop channel is already +// closed. Pass NeverStop to if you don't want it stop. func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) { for { @@ -104,9 +113,11 @@ func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding b } } -// Jitter returns a time.Duration between duration and duration + maxFactor * duration, -// to allow clients to avoid converging on periodic behavior. If maxFactor is 0.0, a -// suggested default value will be chosen. +// Jitter returns a time.Duration between duration and duration + maxFactor * +// duration. +// +// This allows clients to avoid converging on periodic behavior. If maxFactor +// is 0.0, a suggested default value will be chosen. func Jitter(duration time.Duration, maxFactor float64) time.Duration { if maxFactor <= 0.0 { maxFactor = 1.0 @@ -115,26 +126,31 @@ func Jitter(duration time.Duration, maxFactor float64) time.Duration { return wait } -// ErrWaitTimeout is returned when the condition exited without success +// ErrWaitTimeout is returned when the condition exited without success. var ErrWaitTimeout = errors.New("timed out waiting for the condition") // ConditionFunc returns true if the condition is satisfied, or an error // if the loop should be aborted. type ConditionFunc func() (done bool, err error) -// Backoff is parameters applied to a Backoff function. +// Backoff holds parameters applied to a Backoff function. type Backoff struct { - Duration time.Duration - Factor float64 - Jitter float64 - Steps int + Duration time.Duration // the base duration + Factor float64 // Duration is multipled by factor each iteration + Jitter float64 // The amount of jitter applied each iteration + Steps int // Exit with error after this many steps } -// ExponentialBackoff repeats a condition check up to steps times, increasing the wait -// by multipling the previous duration by factor. If jitter is greater than zero, -// a random amount of each duration is added (between duration and duration*(1+jitter)). -// If the condition never returns true, ErrWaitTimeout is returned. All other errors -// terminate immediately. +// ExponentialBackoff repeats a condition check with exponential backoff. +// +// It checks the condition up to Steps times, increasing the wait by multipling +// the previous duration by Factor. +// +// If Jitter is greater than zero, a random amount of each duration is added +// (between duration and duration*(1+jitter)). +// +// If the condition never returns true, ErrWaitTimeout is returned. All other +// errors terminate immediately. func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error { duration := backoff.Duration for i := 0; i < backoff.Steps; i++ { @@ -154,22 +170,33 @@ func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error { } // Poll tries a condition func until it returns true, an error, or the timeout -// is reached. condition will always be invoked at least once but some intervals -// may be missed if the condition takes too long or the time window is too short. +// is reached. +// +// Poll always waits the interval before the run of 'condition'. +// 'condition' will always be invoked at least once. +// +// Some intervals may be missed if the condition takes too long or the time +// window is too short. +// // If you want to Poll something forever, see PollInfinite. -// Poll always waits the interval before the first check of the condition. func Poll(interval, timeout time.Duration, condition ConditionFunc) error { return pollInternal(poller(interval, timeout), condition) } func pollInternal(wait WaitFunc, condition ConditionFunc) error { - done := make(chan struct{}) - defer close(done) - return WaitFor(wait, condition, done) + return WaitFor(wait, condition, NeverStop) } -// PollImmediate is identical to Poll, except that it performs the first check -// immediately, not waiting interval beforehand. +// PollImmediate tries a condition func until it returns true, an error, or the timeout +// is reached. +// +// Poll always checks 'condition' before waiting for the interval. 'condition' +// will always be invoked at least once. +// +// Some intervals may be missed if the condition takes too long or the time +// window is too short. +// +// If you want to Poll something forever, see PollInfinite. func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error { return pollImmediateInternal(poller(interval, timeout), condition) } @@ -185,16 +212,24 @@ func pollImmediateInternal(wait WaitFunc, condition ConditionFunc) error { return pollInternal(wait, condition) } -// PollInfinite polls forever. +// PollInfinite tries a condition func until it returns true or an error +// +// PollInfinite always waits the interval before the run of 'condition'. +// +// Some intervals may be missed if the condition takes too long or the time +// window is too short. func PollInfinite(interval time.Duration, condition ConditionFunc) error { done := make(chan struct{}) defer close(done) return PollUntil(interval, condition, done) } -// PollImmediateInfinite is identical to PollInfinite, except that it -// performs the first check immediately, not waiting interval -// beforehand. +// PollImmediateInfinite tries a condition func until it returns true or an error +// +// PollImmediateInfinite runs the 'condition' before waiting for the interval. +// +// Some intervals may be missed if the condition takes too long or the time +// window is too short. func PollImmediateInfinite(interval time.Duration, condition ConditionFunc) error { done, err := condition() if err != nil { @@ -206,7 +241,11 @@ func PollImmediateInfinite(interval time.Duration, condition ConditionFunc) erro return PollInfinite(interval, condition) } -// PollUntil is like Poll, but it takes a stop change instead of total duration +// PollUntil tries a condition func until it returns true, an error or stopCh is +// closed. +// +// PolUntil always waits interval before the first run of 'condition'. +// 'condition' will always be invoked at least once. func PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error { return WaitFor(poller(interval, 0), condition, stopCh) } @@ -215,11 +254,16 @@ func PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan st // should be executed and is closed when the last test should be invoked. type WaitFunc func(done <-chan struct{}) <-chan struct{} -// WaitFor gets a channel from wait(), and then invokes fn once for every value -// placed on the channel and once more when the channel is closed. If fn -// returns an error the loop ends and that error is returned, and if fn returns -// true the loop ends and nil is returned. ErrWaitTimeout will be returned if -// the channel is closed without fn ever returning true. +// WaitFor continually checks 'fn' as driven by 'wait'. +// +// WaitFor gets a channel from 'wait()'', and then invokes 'fn' once for every value +// placed on the channel and once more when the channel is closed. +// +// If 'fn' returns an error the loop ends and that error is returned, and if +// 'fn' returns true the loop ends and nil is returned. +// +// ErrWaitTimeout will be returned if the channel is closed without fn ever +// returning true. func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error { c := wait(done) for { @@ -238,11 +282,14 @@ func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error { return ErrWaitTimeout } -// poller returns a WaitFunc that will send to the channel every -// interval until timeout has elapsed and then close the channel. -// Over very short intervals you may receive no ticks before -// the channel is closed. If timeout is 0, the channel -// will never be closed. +// poller returns a WaitFunc that will send to the channel every interval until +// timeout has elapsed and then closes the channel. +// +// Over very short intervals you may receive no ticks before the channel is +// closed. A timeout of 0 is interpreted as an infinity. +// +// Output ticks are not buffered. If the channel is not ready to receive an +// item, the tick is skipped. func poller(interval, timeout time.Duration) WaitFunc { return WaitFunc(func(done <-chan struct{}) <-chan struct{} { ch := make(chan struct{})