@@ -253,11 +253,16 @@ cfg_coop! {
253253 use std:: pin:: Pin ;
254254 use std:: task:: { ready, Context , Poll } ;
255255
256+ /// Value returned by the [`poll_proceed`](poll_proceed) method.
257+ #[ derive( Debug ) ]
256258 #[ must_use]
257- pub ( crate ) struct RestoreOnPending ( Cell <Budget >) ;
259+ pub struct RestoreOnPending ( Cell <Budget >) ;
258260
259261 impl RestoreOnPending {
260- pub ( crate ) fn made_progress( & self ) {
262+ /// Signals that the task that obtained this `RestoreOnPending` was able to make
263+ /// progress. This prevents the task budget from being restored to the value
264+ /// it had prior to obtaining this instance when it is dropped.
265+ pub fn made_progress( & self ) {
261266 self . 0 . set( Budget :: unconstrained( ) ) ;
262267 }
263268 }
@@ -275,20 +280,63 @@ cfg_coop! {
275280 }
276281 }
277282
278- /// Returns `Poll::Pending` if the current task has exceeded its budget and should yield.
283+ /// Decrements the task budget and returns `Poll::Pending` if the budget is depleted.
284+ /// This indicates that the task should yield to the scheduler. Otherwise, returns
285+ /// `RestoreOnPending` which can be used to commit the budget consumption.
279286 ///
280- /// When you call this method, the current budget is decremented. However, to ensure that
281- /// progress is made every time a task is polled, the budget is automatically restored to its
282- /// former value if the returned `RestoreOnPending` is dropped. It is the caller's
283- /// responsibility to call `RestoreOnPending::made_progress` if it made progress, to ensure
284- /// that the budget empties appropriately.
287+ /// The returned [`RestoreOnPending`](RestoreOnPending) will revert the budget to its former
288+ /// value when dropped unless [`RestoreOnPending::made_progress`](RestoreOnPending::made_progress)
289+ /// is called. It is the caller's responsibility to do so when it _was_ able to
290+ /// make progress after the call to `poll_pending`.
291+ /// Restoring the budget automatically ensures the task can try to make progress in some other
292+ /// way.
285293 ///
286294 /// Note that `RestoreOnPending` restores the budget **as it was before `poll_proceed`**.
287295 /// Therefore, if the budget is _further_ adjusted between when `poll_proceed` returns and
288- /// `RestRestoreOnPending ` is dropped, those adjustments are erased unless the caller indicates
296+ /// `RestoreOnPending ` is dropped, those adjustments are erased unless the caller indicates
289297 /// that progress was made.
298+ ///
299+ /// # Examples
300+ ///
301+ /// This example shows a `Future` implementation that uses `poll_proceed` to participate in
302+ /// cooperative scheduling. If the work to be done consists of polling another future, consider
303+ /// using the higher-level [`cooperative`](cooperative) function instead.
304+ ///
305+ /// ```ignore
306+ /// use std::future::Future;
307+ /// use std::pin::Pin;
308+ /// use std::task::{ready, Context, Poll};
309+ ///
310+ /// impl<T> Future for CooperativeFuture<T> {
311+ /// type Output = T;
312+ ///
313+ /// fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
314+ /// // `poll_proceed` checks with the runtime if this task is still allowed to proceed
315+ /// // with performing work.
316+ /// // If not, `Pending` is returned and `ready!` ensure this
317+ /// // function returns.
318+ /// // If we are allowed to proceed, coop now represents the budget consumption
319+ /// let coop = ready!(tokio::task::coop::poll_proceed(cx));
320+ ///
321+ /// // Here we try to do some work
322+ /// // If for some reason that's not possible and we exit the function with `Pending`
323+ /// // the drop of `coop` will ensure the consumed budget is restored freeing it up
324+ /// // for use elsewhere.
325+ /// let ret : Poll<T> = ...;
326+ ///
327+ /// // If the value we will return is `Ready(_)` the task did perform some actual work
328+ /// // and the task budget consumption needs to be committed. We do that by calling
329+ /// // `made_progress`. It is essential that this call is made because otherwise the
330+ /// // the budget will be restored and the task may keep running indefinitely.
331+ /// if ret.is_ready() {
332+ /// coop.made_progress();
333+ /// }
334+ /// ret
335+ /// }
336+ /// }
337+ /// ```
290338 #[ inline]
291- pub ( crate ) fn poll_proceed( cx: & mut Context <' _>) -> Poll <RestoreOnPending > {
339+ pub fn poll_proceed( cx: & mut Context <' _>) -> Poll <RestoreOnPending > {
292340 context:: budget( |cell| {
293341 let mut budget = cell. get( ) ;
294342
@@ -388,7 +436,7 @@ cfg_coop! {
388436 /// the result of the inner one. If polling the inner future is pending, polling this future
389437 /// type will also return a `Poll::Pending`.
390438 #[ must_use = "futures do nothing unless polled" ]
391- pub ( crate ) struct Coop <F : Future > {
439+ pub struct Coop <F : Future > {
392440 #[ pin]
393441 pub ( crate ) fut: F ,
394442 }
@@ -413,7 +461,7 @@ cfg_coop! {
413461 /// If the future exceeds its budget while being polled, control is yielded back to the
414462 /// runtime.
415463 #[ inline]
416- pub ( crate ) fn cooperative<F : Future >( fut: F ) -> Coop <F > {
464+ pub fn cooperative<F : Future >( fut: F ) -> Coop <F > {
417465 Coop { fut }
418466 }
419467}
0 commit comments