Skip to content

Commit 11ca716

Browse files
committed
track root task's awake status, and skip blocking on pollables if awake
1 parent e54488f commit 11ca716

File tree

2 files changed

+37
-29
lines changed

2 files changed

+37
-29
lines changed

src/runtime/block_on.rs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use super::{Reactor, REACTOR};
22

3-
use core::future::Future;
4-
use core::pin::pin;
5-
use core::task::Waker;
6-
use core::task::{Context, Poll};
3+
use std::future::Future;
4+
use std::pin::pin;
5+
use std::sync::atomic::{AtomicBool, Ordering};
76
use std::sync::Arc;
8-
use std::task::Wake;
7+
use std::task::{Context, Poll, Wake, Waker};
98

109
/// Start the event loop
1110
pub fn block_on<Fut>(fut: Fut) -> Fut::Output
@@ -24,30 +23,50 @@ where
2423
let mut fut = pin!(fut);
2524

2625
// Create a new context to be passed to the future.
27-
let waker = noop_waker();
26+
let root = Arc::new(RootWaker::new());
27+
let waker = Waker::from(root.clone());
2828
let mut cx = Context::from_waker(&waker);
2929

3030
// Either the future completes and we return, or some IO is happening
3131
// and we wait.
3232
let res = loop {
3333
match fut.as_mut().poll(&mut cx) {
3434
Poll::Ready(res) => break res,
35-
Poll::Pending => reactor.block_until(),
35+
Poll::Pending => {
36+
// If some non-pollable based future has marked the root task
37+
// as awake, reset and poll again. otherwise, block until a
38+
// pollable wakes a future.
39+
if root.is_awake() {
40+
root.reset()
41+
} else {
42+
reactor.block_on_pollables()
43+
}
44+
}
3645
}
3746
};
3847
// Clear the singleton
3948
REACTOR.replace(None);
4049
res
4150
}
4251

43-
/// Construct a new no-op waker
44-
// NOTE: we can remove this once <https://github.com/rust-lang/rust/issues/98286> lands
45-
fn noop_waker() -> Waker {
46-
struct NoopWaker;
47-
48-
impl Wake for NoopWaker {
49-
fn wake(self: Arc<Self>) {}
52+
struct RootWaker {
53+
wake: AtomicBool,
54+
}
55+
impl RootWaker {
56+
fn new() -> Self {
57+
Self {
58+
wake: AtomicBool::new(false),
59+
}
60+
}
61+
fn is_awake(&self) -> bool {
62+
self.wake.load(Ordering::Relaxed)
63+
}
64+
fn reset(&self) {
65+
self.wake.store(false, Ordering::Relaxed);
66+
}
67+
}
68+
impl Wake for RootWaker {
69+
fn wake(self: Arc<Self>) {
70+
self.wake.store(true, Ordering::Relaxed);
5071
}
51-
52-
Waker::from(Arc::new(NoopWaker))
5372
}

src/runtime/reactor.rs

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,8 @@ impl Reactor {
126126
}
127127
}
128128

129-
/// Block until new events are ready. Calls the respective wakers once done.
130-
///
131-
/// # On Wakers and single-threaded runtimes
132-
///
133-
/// At first glance it might seem silly that this goes through the motions
134-
/// of calling the wakers. The main waker we create here is a `noop` waker:
135-
/// it does nothing. However, it is common and encouraged to use wakers to
136-
/// distinguish between events. Concurrency primitives may construct their
137-
/// own wakers to keep track of identity and wake more precisely. We do not
138-
/// control the wakers construted by other libraries, and it is for this
139-
/// reason that we have to call all the wakers - even if by default they
140-
/// will do nothing.
141-
pub(crate) fn block_until(&self) {
129+
/// Block until at least one pending pollable is ready, waking a pending future.
130+
pub(crate) fn block_on_pollables(&self) {
142131
let reactor = self.inner.borrow();
143132

144133
// We're about to wait for a number of pollables. When they wake we get

0 commit comments

Comments
 (0)