'why not panic when task timeout?
I set the timeout to 1s, but the task executes to 3s, but no panic occurs.
#code
#[should_panic]
fn test_timeout() {
let rt = create_runtime();
let timeout_duration = StdDuration::from_secs(1);
let sleep_duration = StdDuration::from_secs(3);
let _guard = rt.enter();
let timeout = time::timeout(timeout_duration, async {
log("timeout running");
thread::sleep(sleep_duration);
log("timeout finsihed");
"Ding!".to_string()
});
rt.block_on(timeout).unwrap();
}
Solution 1:[1]
Using thread::sleep in asynchronous code is almost always wrong.
Conceptually, the timeout works like this:
tokiospawns a timer which would wake up after the specified duration.tokiospawns your future. If it returnsPoll::Ready, timer is thrown away and the future succeeds. If it returnsPoll::Pending,tokiowaits for the next event, i.e. for wakeup of either your future or the timer.- If the future wakes up,
tokiopolls it again. If it returnsPoll::Ready- again, timer is thrown away, future succeeds. - If the timer wakes up,
tokiopolls the future one last time; if it's stillPoll::Pending, it times out and is not polled anymore, andtimeoutreturns an error.
In your case, however, future do not return Poll::Pending - it blocks inside the thread::sleep. So, even though the timer could fire after one second has passed, tokio has no way to react - it waits for the future to return, future returns only after the thread is unblocked, and, since there's no await inside the block, it returns Poll::Ready - so the timer isn't even checked.
To fix this, you're expected to use tokio::time::sleep for any pauses inside async code. With it, the future times out properly. To illustrate this claim, let's see the self-contained example equivalent to your original code:
use core::time::Duration;
use tokio::time::timeout;
#[tokio::main]
async fn main() {
let timeout_duration = Duration::from_secs(1);
let sleep_duration = Duration::from_secs(3);
timeout(timeout_duration, async {
println!("timeout running");
std::thread::sleep(sleep_duration);
println!("timeout finsihed");
"Ding!".to_string()
})
.await
.unwrap_err();
}
As you've already noticed, this fails - unwrap_err panics when called on Ok, and timeout returns Ok since the future didn't time out properly.
But when replacing std::thread::sleep(...) with tokio::time::sleep(...).await...
use core::time::Duration;
use tokio::time::timeout;
#[tokio::main]
async fn main() {
let timeout_duration = Duration::from_secs(1);
let sleep_duration = Duration::from_secs(3);
timeout(timeout_duration, async {
println!("timeout running");
tokio::time::sleep(sleep_duration).await;
println!("timeout finsihed");
"Ding!".to_string()
})
.await
.unwrap_err();
}
...we get the expected behavior - playground.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Cerberus |
