Skip to content

Commit 989e9ef

Browse files
committed
reactor: add a nonblock_check_pollables variant to use when busy
sharing most of the implementation with block_on_pollables
1 parent 1932831 commit 989e9ef

File tree

2 files changed

+54
-9
lines changed

2 files changed

+54
-9
lines changed

src/runtime/block_on.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ where
3737
// as awake, reset and poll again. otherwise, block until a
3838
// pollable wakes a future.
3939
if root.is_awake() {
40+
reactor.nonblock_check_pollables();
4041
root.reset()
4142
} else {
4243
reactor.block_on_pollables()

src/runtime/reactor.rs

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,58 @@ impl Reactor {
128128

129129
/// Block until at least one pending pollable is ready, waking a pending future.
130130
pub(crate) fn block_on_pollables(&self) {
131+
self.check_pollables(|targets| {
132+
debug_assert_ne!(
133+
targets.len(),
134+
0,
135+
"Attempting to block on an empty list of pollables - without any pending work, no progress can be made and wasi::io::poll::poll will trap"
136+
);
137+
wasi::io::poll::poll(targets)
138+
139+
})
140+
}
141+
142+
/// Without blocking, check for any ready pollables and wake the
143+
/// associated futures.
144+
pub(crate) fn nonblock_check_pollables(&self) {
145+
// Lazily create a pollable which always resolves to ready.
146+
use std::sync::LazyLock;
147+
static READY_POLLABLE: LazyLock<Pollable> =
148+
LazyLock::new(|| wasi::clocks::monotonic_clock::subscribe_duration(0));
149+
150+
self.check_pollables(|targets| {
151+
// Create a new set of targets, with the addition of the ready
152+
// pollable:
153+
let ready_index = targets.len();
154+
let mut new_targets = Vec::with_capacity(ready_index + 1);
155+
new_targets.extend_from_slice(targets);
156+
new_targets.push(&*READY_POLLABLE);
157+
158+
// Poll is now guaranteed to return immediately, because at least
159+
// one member is ready:
160+
let mut ready_list = wasi::io::poll::poll(&new_targets);
161+
162+
// Erase our extra ready pollable from the ready list:
163+
ready_list.retain(|e| *e != ready_index as u32);
164+
ready_list
165+
})
166+
}
167+
168+
/// Common core of blocking and nonblocking pollable checks. Wakes any
169+
/// futures which are pending on the pollables, according to the result of
170+
/// the check_ready function.
171+
fn check_pollables<F>(&self, check_ready: F)
172+
where
173+
F: FnOnce(&[&Pollable]) -> Vec<u32>,
174+
{
131175
let reactor = self.inner.borrow();
132176

177+
// If no wakers are pending on pollables, there is no work to be done
178+
// here:
179+
if reactor.wakers.is_empty() {
180+
return;
181+
}
182+
133183
// We're about to wait for a number of pollables. When they wake we get
134184
// the *indexes* back for the pollables whose events were available - so
135185
// we need to be able to associate the index with the right waker.
@@ -144,15 +194,9 @@ impl Reactor {
144194
targets.push(&reactor.pollables[pollable_index.0]);
145195
}
146196

147-
debug_assert_ne!(
148-
targets.len(),
149-
0,
150-
"Attempting to block on an empty list of pollables - without any pending work, no progress can be made and wasi::io::poll::poll will trap"
151-
);
152-
153-
// Now that we have that association, we're ready to poll our targets.
154-
// This will block until an event has completed.
155-
let ready_indexes = wasi::io::poll::poll(&targets);
197+
// Now that we have that association, we're ready to check our targets for readiness.
198+
// (This is either a wasi poll, or the nonblocking variant.)
199+
let ready_indexes = check_ready(&targets);
156200

157201
// Once we have the indexes for which pollables are available, we need
158202
// to convert it back to the right keys for the wakers. Earlier we

0 commit comments

Comments
 (0)