Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TakeUntil + Tokio task budgeting + infinite stream fails #2157

Open
Nemo157 opened this issue May 14, 2020 · 2 comments
Open

TakeUntil + Tokio task budgeting + infinite stream fails #2157

Nemo157 opened this issue May 14, 2020 · 2 comments

Comments

@Nemo157
Copy link
Member

Nemo157 commented May 14, 2020

When using take_until with a Tokio derived future as the condition, wrapping an always ready stream, Tokio's task budgeting will stop the condition from ever completing (playground)

use std::time::Duration;
use futures::stream::StreamExt as _;

#[tokio::main]
async fn main() {
    futures::stream::repeat(0)
        .take_until(tokio::time::delay_for(Duration::from_secs(1)))
        .for_each(|_| async {}).await;
}

What happens is that for the first 128 times the ForEach polls the stream (or whatever the task budget is) the TakeUntil will first poll the Delay, which will return Pending as it is not complete, then poll the Repeat and return an element. After the task budget is exhausted, essentially the same thing will continue happening, but now Delay will be returning Pending because the budget is exhausted. After 1 second, when the Delay should complete, it continues returning Pending because of the budget.

@Darksonn
Copy link
Contributor

Unlike some other issues we've seen, this is not the fault of Tokio's task budgeting, rather this is one of the issues that Tokio's task budgeting fixes. Of course, types that don't participate in the budgeting don't get the benefit, such as not blocking the executor in an example as yours.

To be specific, this is because Tokio's time driver runs on the same thread as where the future is polled, and since you don't yield to the executor, the time driver does not get a chance to notify delay_for.

@Darksonn
Copy link
Contributor

With a closer look, this is related to coop, but it is a different kind of issue from #2047 and #2130, and is more comparable to using executor::block_on. The first two issues would be solved by e.g. notifying the coop task after the call to poll. In this case, it will keep calling poll on the timer in a hot loop until it completes, and it will never yield back to the executor, even if coop doesn't immediately wake it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants