Thoughts about profiling Rust/Tokio applications

This is unfortunately not quite the right conclusion to derive from allocators taking arbitrary time to return. Blocking is defined as a Future that does not periodically yield Poll::Pending. The precise definition of periodicity or duration is debatable.

We can explore a simple example to show that Poll::Pending is what makes a Future non-blocking:

#[tokio::main(flavor = "current_thread")]
async fn main() {
    let _ = tokio::join!(
        tokio::task::spawn(yielding_fut()),
        tokio::task::spawn(blocking_fut()),
    );
}

async fn blocking_fut() {
    println!("This future is blocking...");

    loop {
        std::future::ready(()).await;
    }
}

async fn yielding_fut() {
    tokio::task::yield_now().await;

    println!("... which prevents this future from making any progress!");
}

The second line will never be printed, even though there is clearly an await keyword in the blocking future! Why is it blocking? Because it never returns Poll::Pending. If it is modified to periodically return Poll::Pending, as shown in this example: Strange tokio select behavior - #3 by parasyte you will eventually see the second task make progress (and run to completion).

The fact that some functions take "some time" to return is undesirable and contributes to the periodic property of blocking. But it is not the definition of what blocks a cooperative scheduler.

Therefore, non-blocking is a property of the function, not the system.

1 Like