-
Notifications
You must be signed in to change notification settings - Fork 149
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
Dropped timer causes infinite wakeups? #113
Comments
Slight minimization, it's not unique to Stream: use smol::{self, Timer};
use std::task::Poll;
use std::time::Duration;
use futures::future::{FutureExt, self};
pub fn main() {
smol::run(
future::poll_fn(|cx| {
println!("I am being polled");
Timer::after(Duration::from_secs(1)).poll_unpin(cx);
Poll::<Option<()>>::Pending
})
);
} But I think it's inaccurate to say this triggers an infinite polling loop, just a single spurious poll. This example code creates and drops the timer every time it's polled, which is causing the tight loop. |
When a timer is inserted into the reactor, a message is be sent to the timer_event object so that someone waiting in the reactor gets woken up early so that they can consider if the new timer is will trigger before they would have originally woken up. However, if no one is waiting in the reactor, this step is unnecessary. In a case with only one executor thread, this is always true, since timers aren't added while the reactor lock is held. Furthermore, if you notify the reactor while no one is holding the lock, the next reactor wait will return immediately, which will make you go through the event loop again unnecessarily. Fixes smol-rs#113
When a timer is inserted into the reactor, a message is sent to the timer_event object so that someone waiting in the reactor gets woken up early so that they can consider if the new timer is will trigger before they would have originally woken up. However, if no one is waiting in the reactor, this step is unnecessary. In a case with only one executor thread, this is always true, since timers aren't added while the reactor lock is held. Furthermore, if you notify the reactor while no one is holding the lock, the next reactor wait will return immediately, which will make you go through the event loop again unnecessarily. Fixes smol-rs#113
I think there's 2 "bugs" this reveals in smol:
I believe fixing either of these things will make this "bug" disappear. That said, futures getting polled spuriously is fairly normal when multiple futures are combined together with select or join, so I'm not sure that this bug itself represents a real issue. It was fun to dig in to smol to try to figure out why this was happening, regardless. |
Oh, and I found using a variant of the repro case that exits after 20 polls more pleasant to work with, in case that's useful: use smol::{self, Timer};
use std::task::Poll;
use std::time::Duration;
use futures::future::{self, FutureExt};
pub fn main() {
let mut poll_count: i32 = 0;
smol::run(future::poll_fn(|cx| {
poll_count += 1;
if poll_count == 20 {
return Poll::Ready(());
}
eprintln!("polled {} times", poll_count);
Timer::after(Duration::from_secs(1)).poll_unpin(cx);
Poll::Pending
}));
} |
Thank you for the deep analysis! This will be fixed after I extract the |
I think this is actually fixed by 55f360b At the very least, I can no longer reproduce it. I think the added The other bug I mentioned (main future gets polled regardless of being woken) still exists, but it might be worthwhile to split that out into a separate bug, so I opened #166 |
Great, thanks! |
I'd expect this incorrect program to never wake up, never return and underneath stream::poll_fn to be polled once.
When running with smol 0.1.5 it triggers tight infinite polling loop.
For comparison, here is Playground with tokio: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=489752594073ad372fee0a65f0aeb7ad
The text was updated successfully, but these errors were encountered: