mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-07 00:00:08 +02:00
runtime: reset woken of outer future after polled (#4157)
This commit is contained in:
@@ -12,7 +12,7 @@ use std::cell::RefCell;
|
|||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::future::Future;
|
use std::future::Future;
|
||||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
use std::sync::atomic::Ordering::{AcqRel, Release};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::task::Poll::{Pending, Ready};
|
use std::task::Poll::{Pending, Ready};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@@ -219,13 +219,11 @@ impl<P: Park> Inner<P> {
|
|||||||
let _enter = crate::runtime::enter(false);
|
let _enter = crate::runtime::enter(false);
|
||||||
let waker = scheduler.spawner.waker_ref();
|
let waker = scheduler.spawner.waker_ref();
|
||||||
let mut cx = std::task::Context::from_waker(&waker);
|
let mut cx = std::task::Context::from_waker(&waker);
|
||||||
let mut polled = false;
|
|
||||||
|
|
||||||
pin!(future);
|
pin!(future);
|
||||||
|
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
if scheduler.spawner.was_woken() || !polled {
|
if scheduler.spawner.reset_woken() {
|
||||||
polled = true;
|
|
||||||
scheduler.stats.incr_poll_count();
|
scheduler.stats.incr_poll_count();
|
||||||
if let Ready(v) = crate::coop::budget(|| future.as_mut().poll(&mut cx)) {
|
if let Ready(v) = crate::coop::budget(|| future.as_mut().poll(&mut cx)) {
|
||||||
return v;
|
return v;
|
||||||
@@ -418,13 +416,15 @@ impl Spawner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn waker_ref(&self) -> WakerRef<'_> {
|
fn waker_ref(&self) -> WakerRef<'_> {
|
||||||
// clear the woken bit
|
// Set woken to true when enter block_on, ensure outer future
|
||||||
self.shared.woken.swap(false, AcqRel);
|
// be polled for the first time when enter loop
|
||||||
|
self.shared.woken.store(true, Release);
|
||||||
waker_ref(&self.shared)
|
waker_ref(&self.shared)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn was_woken(&self) -> bool {
|
// reset woken to false and return original value
|
||||||
self.shared.woken.load(Acquire)
|
pub(crate) fn reset_woken(&self) -> bool {
|
||||||
|
self.shared.woken.swap(false, AcqRel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,45 @@ fn block_on_num_polls() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn assert_no_unnecessary_polls() {
|
||||||
|
loom::model(|| {
|
||||||
|
// // After we poll outer future, woken should reset to false
|
||||||
|
let rt = Builder::new_current_thread().build().unwrap();
|
||||||
|
let (tx, rx) = oneshot::channel();
|
||||||
|
let pending_cnt = Arc::new(AtomicUsize::new(0));
|
||||||
|
|
||||||
|
rt.spawn(async move {
|
||||||
|
for _ in 0..24 {
|
||||||
|
task::yield_now().await;
|
||||||
|
}
|
||||||
|
tx.send(()).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
let pending_cnt_clone = pending_cnt.clone();
|
||||||
|
rt.block_on(async move {
|
||||||
|
// use task::yield_now() to ensure woken set to true
|
||||||
|
// ResetFuture will be polled at most once
|
||||||
|
// Here comes two cases
|
||||||
|
// 1. recv no message from channel, ResetFuture will be polled
|
||||||
|
// but get Pending and we record ResetFuture.pending_cnt ++.
|
||||||
|
// Then when message arrive, ResetFuture returns Ready. So we
|
||||||
|
// expect ResetFuture.pending_cnt = 1
|
||||||
|
// 2. recv message from channel, ResetFuture returns Ready immediately.
|
||||||
|
// We expect ResetFuture.pending_cnt = 0
|
||||||
|
task::yield_now().await;
|
||||||
|
ResetFuture {
|
||||||
|
rx,
|
||||||
|
pending_cnt: pending_cnt_clone,
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
});
|
||||||
|
|
||||||
|
let pending_cnt = pending_cnt.load(Acquire);
|
||||||
|
assert!(pending_cnt <= 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
struct BlockedFuture {
|
struct BlockedFuture {
|
||||||
rx: Receiver<()>,
|
rx: Receiver<()>,
|
||||||
num_polls: Arc<AtomicUsize>,
|
num_polls: Arc<AtomicUsize>,
|
||||||
@@ -80,3 +119,22 @@ impl Future for BlockedFuture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ResetFuture {
|
||||||
|
rx: Receiver<()>,
|
||||||
|
pending_cnt: Arc<AtomicUsize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Future for ResetFuture {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
match Pin::new(&mut self.rx).poll(cx) {
|
||||||
|
Poll::Pending => {
|
||||||
|
self.pending_cnt.fetch_add(1, Release);
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
_ => Poll::Ready(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user