Skip to content

Commit 4ad718d

Browse files
committed
Make cooperative and poll_proceed public
1 parent 933fa49 commit 4ad718d

File tree

1 file changed

+60
-12
lines changed

1 file changed

+60
-12
lines changed

tokio/src/task/coop/mod.rs

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)