Module coop

Source
Expand description

Utilities for improved cooperative scheduling.

Β§Cooperative scheduling

A single call to poll on a top-level task may potentially do a lot of work before it returns Poll::Pending. If a task runs for a long period of time without yielding back to the executor, it can starve other tasks waiting on that executor to execute them, or drive underlying resources. Since Rust does not have a runtime, it is difficult to forcibly preempt a long-running task. Instead, this module provides an opt-in mechanism for futures to collaborate with the executor to avoid starvation.

Consider a future like this one:

async fn drop_all<I: Stream + Unpin>(mut input: I) {
    while let Some(_) = input.next().await {}
}

It may look harmless, but consider what happens under heavy load if the input stream is always ready. If we spawn drop_all, the task will never yield, and will starve other tasks and resources on the same executor.

To account for this, Tokio has explicit yield points in a number of library functions, which force tasks to return to the executor periodically.

Β§unconstrained

If necessary, task::unconstrained lets you opt a future out of Tokio’s cooperative scheduling. When a future is wrapped with unconstrained, it will never be forced to yield to Tokio. For example:

use tokio::{task, sync::mpsc};

let fut = async {
    let (tx, mut rx) = mpsc::unbounded_channel();

    for i in 0..1000 {
        let _ = tx.send(());
        // This will always be ready. If coop was in effect, this code would be forced to yield
        // periodically. However, if left unconstrained, then this code will never yield.
        rx.recv().await;
    }
};

task::coop::unconstrained(fut).await;

ModulesΒ§

consume_budget πŸ”’
unconstrained πŸ”’

StructsΒ§

Budget πŸ”’
Opaque type tracking the amount of β€œwork” a task may still do before yielding back to the scheduler.
BudgetDecrement πŸ”’
Coop πŸ”’
Future wrapper to ensure cooperative scheduling.
RestoreOnPending πŸ”’
Unconstrained
Future for the unconstrained method.

FunctionsΒ§

budget πŸ”’
Runs the given closure with a cooperative task budget. When the function returns, the budget is reset to the value prior to calling the function.
consume_budget
Consumes a unit of budget and returns the execution back to the Tokio runtime if the task’s coop budget was exhausted.
cooperative πŸ”’
Run a future with a budget constraint for cooperative scheduling. If the future exceeds its budget while being polled, control is yielded back to the runtime.
has_budget_remaining
Returns true if there is still budget left on the task.
inc_budget_forced_yield_count πŸ”’
poll_budget_available πŸ”’
Returns Poll::Ready if the current task has budget to consume, and Poll::Pending otherwise.
poll_proceed πŸ”’
Returns Poll::Pending if the current task has exceeded its budget and should yield.
register_waker πŸ”’
set πŸ”’
Sets the current task’s budget.
stop πŸ”’
Forcibly removes the budgeting constraints early.
unconstrained
Turn off cooperative scheduling for a future. The future will never be forced to yield by Tokio. Using this exposes your service to starvation if the unconstrained future never yields otherwise.
with_budget πŸ”’
with_unconstrained πŸ”’
Runs the given closure with an unconstrained task budget. When the function returns, the budget is reset to the value prior to calling the function.