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 }