Skip to content

Commit 097effa

Browse files
authored
Add runtime::spawn for WASIp3 (#156)
1 parent 13202f5 commit 097effa

4 files changed

Lines changed: 116 additions & 16 deletions

File tree

‎Cargo.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ serde_json = { workspace = true, optional = true }
3737
wasip2.workspace = true
3838

3939
[target.'cfg(all(target_os = "wasi", target_env = "p3"))'.dependencies]
40-
wasip3.workspace = true
40+
wasip3 = { workspace = true, features = ["async-spawn"] }
4141

4242
[dev-dependencies]
4343
anyhow.workspace = true

‎src/lib.rs‎

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,7 @@ pub mod iter;
6767
pub mod net;
6868
#[cfg(target_os = "wasi")]
6969
pub mod rand;
70-
#[cfg(all(target_os = "wasi", target_env = "p2"))]
7170
pub mod runtime;
72-
#[cfg(all(target_os = "wasi", target_env = "p3"))]
73-
pub mod runtime {
74-
pub fn block_on<F, T>(fut: F) -> F::Output
75-
where
76-
F: Future<Output = T>,
77-
T: 'static,
78-
{
79-
wasip3::wit_bindgen::block_on(fut)
80-
}
81-
}
8271
#[cfg(all(target_os = "wasi", target_env = "p2"))]
8372
pub mod task;
8473
#[cfg(all(target_os = "wasi", target_env = "p2"))]

‎src/runtime/mod.rs‎

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,71 @@
11
//! Async event loop support.
22
//!
3-
//! The way to use this is to call [`block_on()`]. Inside the future, [`Reactor::current`]
4-
//! will give an instance of the [`Reactor`] running the event loop, which can be
5-
//! to [`AsyncPollable::wait_for`] instances of
3+
//! On WASI 0.2 the way to use this is to call [`block_on()`]. Inside the
4+
//! future, [`Reactor::current`] will give an instance of the [`Reactor`]
5+
//! running the event loop, which can be used to [`AsyncPollable::wait_for`]
6+
//! instances of
67
//! [`wasip2::Pollable`](https://docs.rs/wasi/latest/wasi/io/poll/struct.Pollable.html).
78
//! This will automatically wait for the futures to resolve, and call the
89
//! necessary wakers to work.
10+
//!
11+
//! On WASI 0.3 [`block_on`] can be used to drive a future, but an async
12+
//! function can also be directly exported and will be driven by the host.
913
1014
#![deny(missing_debug_implementations, nonstandard_style)]
1115
#![warn(missing_docs, unreachable_pub)]
1216

17+
pub use ::async_task::Task;
18+
19+
#[cfg(target_env = "p2")]
1320
mod block_on;
21+
#[cfg(target_env = "p2")]
1422
mod reactor;
1523

16-
pub use ::async_task::Task;
24+
#[cfg(target_env = "p2")]
1725
pub use block_on::block_on;
26+
#[cfg(target_env = "p2")]
1827
pub use reactor::{AsyncPollable, Reactor, WaitFor};
28+
#[cfg(target_env = "p2")]
1929
use std::cell::RefCell;
2030

2131
// There are no threads in WASI 0.2, so this is just a safe way to thread a single reactor to all
2232
// use sites in the background.
33+
#[cfg(target_env = "p2")]
2334
std::thread_local! {
2435
pub(crate) static REACTOR: RefCell<Option<Reactor>> = const { RefCell::new(None) };
2536
}
2637

2738
/// Spawn a `Future` as a `Task` on the current `Reactor`.
2839
///
2940
/// Panics if called from outside `block_on`.
41+
#[cfg(target_env = "p2")]
3042
pub fn spawn<F, T>(fut: F) -> Task<T>
3143
where
3244
F: std::future::Future<Output = T> + 'static,
3345
T: 'static,
3446
{
3547
Reactor::current().spawn(fut)
3648
}
49+
50+
#[cfg(target_env = "p3")]
51+
pub use ::async_task::Runnable;
52+
#[cfg(target_env = "p3")]
53+
pub use wasip3::wit_bindgen::block_on;
54+
55+
/// Spawn a `Future` as a `Task` on the WASI 0.3 async runtime.
56+
#[cfg(target_env = "p3")]
57+
pub fn spawn<F, T>(fut: F) -> Task<T>
58+
where
59+
F: std::future::Future<Output = T> + 'static,
60+
T: 'static,
61+
{
62+
let (runnable, task) = async_task::spawn_local(fut, |runnable: Runnable| {
63+
// Scheduling the task is accomplished by spawning a future which
64+
// executes the `run` method.
65+
wasip3::spawn_local(async move {
66+
let _ = runnable.run();
67+
});
68+
});
69+
runnable.schedule();
70+
task
71+
}

‎tests/runtime_spawn.rs‎

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use std::cell::Cell;
2+
use std::future::pending;
3+
use std::rc::Rc;
4+
5+
use futures_lite::future::yield_now;
6+
7+
struct DropFlag(Rc<Cell<bool>>);
8+
9+
impl Drop for DropFlag {
10+
fn drop(&mut self) {
11+
self.0.set(true);
12+
}
13+
}
14+
15+
async fn wait_until(flag: &Cell<bool>) {
16+
while !flag.get() {
17+
yield_now().await;
18+
}
19+
}
20+
21+
#[wstd::test]
22+
async fn spawn_detach_cancel_and_drop() {
23+
assert_eq!(wstd::runtime::spawn(async { 42 }).await, 42);
24+
25+
// Check that a detached task completes eventually.
26+
let detached_completed = Rc::new(Cell::new(false));
27+
let completed = detached_completed.clone();
28+
wstd::runtime::spawn(async move {
29+
yield_now().await;
30+
completed.set(true);
31+
})
32+
.detach();
33+
34+
assert!(!detached_completed.get());
35+
wait_until(&detached_completed).await;
36+
assert!(detached_completed.get());
37+
38+
// Check that calling `cancel` on a task cancels and drops the spawned
39+
// future.
40+
let canceled_started = Rc::new(Cell::new(false));
41+
let canceled_dropped = Rc::new(Cell::new(false));
42+
let canceled_completed = Rc::new(Cell::new(false));
43+
let started = canceled_started.clone();
44+
let dropped = canceled_dropped.clone();
45+
let completed = canceled_completed.clone();
46+
let task = wstd::runtime::spawn(async move {
47+
let _drop_flag = DropFlag(dropped);
48+
started.set(true);
49+
pending::<()>().await;
50+
completed.set(true);
51+
});
52+
53+
wait_until(&canceled_started).await;
54+
assert_eq!(task.cancel().await, None);
55+
assert!(canceled_dropped.get());
56+
assert!(!canceled_completed.get());
57+
58+
// Check that dropping a task cancels and drops the spawned future.
59+
let dropped_started = Rc::new(Cell::new(false));
60+
let dropped_dropped = Rc::new(Cell::new(false));
61+
let dropped_completed = Rc::new(Cell::new(false));
62+
let started = dropped_started.clone();
63+
let dropped = dropped_dropped.clone();
64+
let completed = dropped_completed.clone();
65+
let task = wstd::runtime::spawn(async move {
66+
let _drop_flag = DropFlag(dropped);
67+
started.set(true);
68+
pending::<()>().await;
69+
completed.set(true);
70+
});
71+
72+
wait_until(&dropped_started).await;
73+
drop(task);
74+
wait_until(&dropped_dropped).await;
75+
assert!(!dropped_completed.get());
76+
}

0 commit comments

Comments
 (0)