...

Source file src/k8s.io/apimachinery/pkg/util/wait/poll.go

Documentation: k8s.io/apimachinery/pkg/util/wait

     1  /*
     2  Copyright 2023 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  	"time"
    22  )
    23  
    24  // PollUntilContextCancel tries a condition func until it returns true, an error, or the context
    25  // is cancelled or hits a deadline. condition will be invoked after the first interval if the
    26  // context is not cancelled first. The returned error will be from ctx.Err(), the condition's
    27  // err return value, or nil. If invoking condition takes longer than interval the next condition
    28  // will be invoked immediately. When using very short intervals, condition may be invoked multiple
    29  // times before a context cancellation is detected. If immediate is true, condition will be
    30  // invoked before waiting and guarantees that condition is invoked at least once, regardless of
    31  // whether the context has been cancelled.
    32  func PollUntilContextCancel(ctx context.Context, interval time.Duration, immediate bool, condition ConditionWithContextFunc) error {
    33  	return loopConditionUntilContext(ctx, Backoff{Duration: interval}.Timer(), immediate, false, condition)
    34  }
    35  
    36  // PollUntilContextTimeout will terminate polling after timeout duration by setting a context
    37  // timeout. This is provided as a convenience function for callers not currently executing under
    38  // a deadline and is equivalent to:
    39  //
    40  //	deadlineCtx, deadlineCancel := context.WithTimeout(ctx, timeout)
    41  //	err := PollUntilContextCancel(deadlineCtx, interval, immediate, condition)
    42  //
    43  // The deadline context will be cancelled if the Poll succeeds before the timeout, simplifying
    44  // inline usage. All other behavior is identical to PollUntilContextCancel.
    45  func PollUntilContextTimeout(ctx context.Context, interval, timeout time.Duration, immediate bool, condition ConditionWithContextFunc) error {
    46  	deadlineCtx, deadlineCancel := context.WithTimeout(ctx, timeout)
    47  	defer deadlineCancel()
    48  	return loopConditionUntilContext(deadlineCtx, Backoff{Duration: interval}.Timer(), immediate, false, condition)
    49  }
    50  
    51  // Poll tries a condition func until it returns true, an error, or the timeout
    52  // is reached.
    53  //
    54  // Poll always waits the interval before the run of 'condition'.
    55  // 'condition' will always be invoked at least once.
    56  //
    57  // Some intervals may be missed if the condition takes too long or the time
    58  // window is too short.
    59  //
    60  // If you want to Poll something forever, see PollInfinite.
    61  //
    62  // Deprecated: This method does not return errors from context, use PollUntilContextTimeout.
    63  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
    64  // defined by the context package. Will be removed in a future release.
    65  func Poll(interval, timeout time.Duration, condition ConditionFunc) error {
    66  	return PollWithContext(context.Background(), interval, timeout, condition.WithContext())
    67  }
    68  
    69  // PollWithContext tries a condition func until it returns true, an error,
    70  // or when the context expires or the timeout is reached, whichever
    71  // happens first.
    72  //
    73  // PollWithContext always waits the interval before the run of 'condition'.
    74  // 'condition' will always be invoked at least once.
    75  //
    76  // Some intervals may be missed if the condition takes too long or the time
    77  // window is too short.
    78  //
    79  // If you want to Poll something forever, see PollInfinite.
    80  //
    81  // Deprecated: This method does not return errors from context, use PollUntilContextTimeout.
    82  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
    83  // defined by the context package. Will be removed in a future release.
    84  func PollWithContext(ctx context.Context, interval, timeout time.Duration, condition ConditionWithContextFunc) error {
    85  	return poll(ctx, false, poller(interval, timeout), condition)
    86  }
    87  
    88  // PollUntil tries a condition func until it returns true, an error or stopCh is
    89  // closed.
    90  //
    91  // PollUntil always waits interval before the first run of 'condition'.
    92  // 'condition' will always be invoked at least once.
    93  //
    94  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
    95  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
    96  // defined by the context package. Will be removed in a future release.
    97  func PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
    98  	return PollUntilWithContext(ContextForChannel(stopCh), interval, condition.WithContext())
    99  }
   100  
   101  // PollUntilWithContext tries a condition func until it returns true,
   102  // an error or the specified context is cancelled or expired.
   103  //
   104  // PollUntilWithContext always waits interval before the first run of 'condition'.
   105  // 'condition' will always be invoked at least once.
   106  //
   107  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   108  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   109  // defined by the context package. Will be removed in a future release.
   110  func PollUntilWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   111  	return poll(ctx, false, poller(interval, 0), condition)
   112  }
   113  
   114  // PollInfinite tries a condition func until it returns true or an error
   115  //
   116  // PollInfinite always waits the interval before the run of 'condition'.
   117  //
   118  // Some intervals may be missed if the condition takes too long or the time
   119  // window is too short.
   120  //
   121  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   122  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   123  // defined by the context package. Will be removed in a future release.
   124  func PollInfinite(interval time.Duration, condition ConditionFunc) error {
   125  	return PollInfiniteWithContext(context.Background(), interval, condition.WithContext())
   126  }
   127  
   128  // PollInfiniteWithContext tries a condition func until it returns true or an error
   129  //
   130  // PollInfiniteWithContext always waits the interval before the run of 'condition'.
   131  //
   132  // Some intervals may be missed if the condition takes too long or the time
   133  // window is too short.
   134  //
   135  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   136  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   137  // defined by the context package. Will be removed in a future release.
   138  func PollInfiniteWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   139  	return poll(ctx, false, poller(interval, 0), condition)
   140  }
   141  
   142  // PollImmediate tries a condition func until it returns true, an error, or the timeout
   143  // is reached.
   144  //
   145  // PollImmediate always checks 'condition' before waiting for the interval. 'condition'
   146  // will always be invoked at least once.
   147  //
   148  // Some intervals may be missed if the condition takes too long or the time
   149  // window is too short.
   150  //
   151  // If you want to immediately Poll something forever, see PollImmediateInfinite.
   152  //
   153  // Deprecated: This method does not return errors from context, use PollUntilContextTimeout.
   154  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   155  // defined by the context package. Will be removed in a future release.
   156  func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error {
   157  	return PollImmediateWithContext(context.Background(), interval, timeout, condition.WithContext())
   158  }
   159  
   160  // PollImmediateWithContext tries a condition func until it returns true, an error,
   161  // or the timeout is reached or the specified context expires, whichever happens first.
   162  //
   163  // PollImmediateWithContext always checks 'condition' before waiting for the interval.
   164  // 'condition' will always be invoked at least once.
   165  //
   166  // Some intervals may be missed if the condition takes too long or the time
   167  // window is too short.
   168  //
   169  // If you want to immediately Poll something forever, see PollImmediateInfinite.
   170  //
   171  // Deprecated: This method does not return errors from context, use PollUntilContextTimeout.
   172  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   173  // defined by the context package. Will be removed in a future release.
   174  func PollImmediateWithContext(ctx context.Context, interval, timeout time.Duration, condition ConditionWithContextFunc) error {
   175  	return poll(ctx, true, poller(interval, timeout), condition)
   176  }
   177  
   178  // PollImmediateUntil tries a condition func until it returns true, an error or stopCh is closed.
   179  //
   180  // PollImmediateUntil runs the 'condition' before waiting for the interval.
   181  // 'condition' will always be invoked at least once.
   182  //
   183  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   184  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   185  // defined by the context package. Will be removed in a future release.
   186  func PollImmediateUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
   187  	return PollImmediateUntilWithContext(ContextForChannel(stopCh), interval, condition.WithContext())
   188  }
   189  
   190  // PollImmediateUntilWithContext tries a condition func until it returns true,
   191  // an error or the specified context is cancelled or expired.
   192  //
   193  // PollImmediateUntilWithContext runs the 'condition' before waiting for the interval.
   194  // 'condition' will always be invoked at least once.
   195  //
   196  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   197  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   198  // defined by the context package. Will be removed in a future release.
   199  func PollImmediateUntilWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   200  	return poll(ctx, true, poller(interval, 0), condition)
   201  }
   202  
   203  // PollImmediateInfinite tries a condition func until it returns true or an error
   204  //
   205  // PollImmediateInfinite runs the 'condition' before waiting for the interval.
   206  //
   207  // Some intervals may be missed if the condition takes too long or the time
   208  // window is too short.
   209  //
   210  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   211  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   212  // defined by the context package. Will be removed in a future release.
   213  func PollImmediateInfinite(interval time.Duration, condition ConditionFunc) error {
   214  	return PollImmediateInfiniteWithContext(context.Background(), interval, condition.WithContext())
   215  }
   216  
   217  // PollImmediateInfiniteWithContext tries a condition func until it returns true
   218  // or an error or the specified context gets cancelled or expired.
   219  //
   220  // PollImmediateInfiniteWithContext runs the 'condition' before waiting for the interval.
   221  //
   222  // Some intervals may be missed if the condition takes too long or the time
   223  // window is too short.
   224  //
   225  // Deprecated: This method does not return errors from context, use PollUntilContextCancel.
   226  // Note that the new method will no longer return ErrWaitTimeout and instead return errors
   227  // defined by the context package. Will be removed in a future release.
   228  func PollImmediateInfiniteWithContext(ctx context.Context, interval time.Duration, condition ConditionWithContextFunc) error {
   229  	return poll(ctx, true, poller(interval, 0), condition)
   230  }
   231  
   232  // Internally used, each of the public 'Poll*' function defined in this
   233  // package should invoke this internal function with appropriate parameters.
   234  // ctx: the context specified by the caller, for infinite polling pass
   235  // a context that never gets cancelled or expired.
   236  // immediate: if true, the 'condition' will be invoked before waiting for the interval,
   237  // in this case 'condition' will always be invoked at least once.
   238  // wait: user specified WaitFunc function that controls at what interval the condition
   239  // function should be invoked periodically and whether it is bound by a timeout.
   240  // condition: user specified ConditionWithContextFunc function.
   241  //
   242  // Deprecated: will be removed in favor of loopConditionUntilContext.
   243  func poll(ctx context.Context, immediate bool, wait waitWithContextFunc, condition ConditionWithContextFunc) error {
   244  	if immediate {
   245  		done, err := runConditionWithCrashProtectionWithContext(ctx, condition)
   246  		if err != nil {
   247  			return err
   248  		}
   249  		if done {
   250  			return nil
   251  		}
   252  	}
   253  
   254  	select {
   255  	case <-ctx.Done():
   256  		// returning ctx.Err() will break backward compatibility, use new PollUntilContext*
   257  		// methods instead
   258  		return ErrWaitTimeout
   259  	default:
   260  		return waitForWithContext(ctx, wait, condition)
   261  	}
   262  }
   263  
   264  // poller returns a WaitFunc that will send to the channel every interval until
   265  // timeout has elapsed and then closes the channel.
   266  //
   267  // Over very short intervals you may receive no ticks before the channel is
   268  // closed. A timeout of 0 is interpreted as an infinity, and in such a case
   269  // it would be the caller's responsibility to close the done channel.
   270  // Failure to do so would result in a leaked goroutine.
   271  //
   272  // Output ticks are not buffered. If the channel is not ready to receive an
   273  // item, the tick is skipped.
   274  //
   275  // Deprecated: Will be removed in a future release.
   276  func poller(interval, timeout time.Duration) waitWithContextFunc {
   277  	return waitWithContextFunc(func(ctx context.Context) <-chan struct{} {
   278  		ch := make(chan struct{})
   279  
   280  		go func() {
   281  			defer close(ch)
   282  
   283  			tick := time.NewTicker(interval)
   284  			defer tick.Stop()
   285  
   286  			var after <-chan time.Time
   287  			if timeout != 0 {
   288  				// time.After is more convenient, but it
   289  				// potentially leaves timers around much longer
   290  				// than necessary if we exit early.
   291  				timer := time.NewTimer(timeout)
   292  				after = timer.C
   293  				defer timer.Stop()
   294  			}
   295  
   296  			for {
   297  				select {
   298  				case <-tick.C:
   299  					// If the consumer isn't ready for this signal drop it and
   300  					// check the other channels.
   301  					select {
   302  					case ch <- struct{}{}:
   303  					default:
   304  					}
   305  				case <-after:
   306  					return
   307  				case <-ctx.Done():
   308  					return
   309  				}
   310  			}
   311  		}()
   312  
   313  		return ch
   314  	})
   315  }
   316  

View as plain text