poll.go 14 KB

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