Home > Back-end >  What is the meaing of yield on tokio async rust?
What is the meaing of yield on tokio async rust?

Time:01-19

When reading Tokio rust documentation, it talks about yielding control back to the thread. Does this means that the function ended his execution and has returned a value?

The exact quote from tokio.rs is:

The async fn definition looks like a regular synchronous function, but operates asynchronously. Rust transforms the async fn at compile time into a routine that operates asynchronously. Any calls to .await within the async fn yield control back to the thread. The thread may do other work while the operation processes in the background.

CodePudding user response:

You can think of tokio (and other async runtimes) as schedulers and async functions as tasks to be completed. In this context, 'yeild' means a running task can decide to pause execution to give control back to the scheduler so the thread can be used to work on another task (if there are other tasks queued). If there are no other tasks available, then the scheduler may decide to continue the previous task. The task does not end. It is more like we save our place within the task so we can go back to it later.

So when you call an async function, you are essentially giving a new task to the scheduler. Then when you use .await you are telling the scheduler that you want to wait for the result of that task. This makes yielding desirable since there is nothing we can do until the other task is completed and we want to free up resources so that other tasks can be worked on while we wait.

CodePudding user response:

It has not ended, just paused. It will continue when the future will be polled again.

Here's an example:

use std::future::Future;
use std::task::{Context, Poll};

async fn bar() {
    // Force to yield back to the caller.
    let mut yielded = false;
    std::future::poll_fn(move |_| {
        if !yielded {
            yielded = true;
            Poll::Pending
        } else {
            Poll::Ready(())
        }
    })
    .await;
}

async fn foo() {
    println!("before");
    bar().await;
    println!("after");
}

fn main() {
    let waker = futures::task::noop_waker();
    let mut context = Context::from_waker(&waker);

    let mut future = Box::pin(foo());
    // First, poll the future once.
    assert_eq!(future.as_mut().poll(&mut context), Poll::Pending);
    // Now the future is paused at the `.await` inside `bar()`. Poll it again so it completes.
    assert_eq!(future.as_mut().poll(&mut context), Poll::Ready(()));
}

Playground.

Usually, the Waker registers to future to be polled again when some event happens, and then the scheduler poll it when the even happens. For example, if you do I/O in tokio, the waker given to your future will register it to be polled again when the I/O is ready.

  • Related