-
Notifications
You must be signed in to change notification settings - Fork 19
Add runtime::spawn for WASIp3
#156
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,36 +1,71 @@ | ||
| //! Async event loop support. | ||
| //! | ||
| //! The way to use this is to call [`block_on()`]. Inside the future, [`Reactor::current`] | ||
| //! will give an instance of the [`Reactor`] running the event loop, which can be | ||
| //! to [`AsyncPollable::wait_for`] instances of | ||
| //! On WASI 0.2 the way to use this is to call [`block_on()`]. Inside the | ||
| //! future, [`Reactor::current`] will give an instance of the [`Reactor`] | ||
| //! running the event loop, which can be used to [`AsyncPollable::wait_for`] | ||
| //! instances of | ||
| //! [`wasip2::Pollable`](https://docs.rs/wasi/latest/wasi/io/poll/struct.Pollable.html). | ||
| //! This will automatically wait for the futures to resolve, and call the | ||
| //! necessary wakers to work. | ||
| //! | ||
| //! On WASI 0.3 [`block_on`] can be used to drive a future, but an async | ||
| //! function can also be directly exported and will be driven by the host. | ||
|
|
||
| #![deny(missing_debug_implementations, nonstandard_style)] | ||
| #![warn(missing_docs, unreachable_pub)] | ||
|
|
||
| pub use ::async_task::Task; | ||
|
|
||
| #[cfg(target_env = "p2")] | ||
| mod block_on; | ||
| #[cfg(target_env = "p2")] | ||
| mod reactor; | ||
|
|
||
| pub use ::async_task::Task; | ||
| #[cfg(target_env = "p2")] | ||
| pub use block_on::block_on; | ||
| #[cfg(target_env = "p2")] | ||
| pub use reactor::{AsyncPollable, Reactor, WaitFor}; | ||
| #[cfg(target_env = "p2")] | ||
| use std::cell::RefCell; | ||
|
|
||
| // There are no threads in WASI 0.2, so this is just a safe way to thread a single reactor to all | ||
| // use sites in the background. | ||
| #[cfg(target_env = "p2")] | ||
| std::thread_local! { | ||
| pub(crate) static REACTOR: RefCell<Option<Reactor>> = const { RefCell::new(None) }; | ||
| } | ||
|
|
||
| /// Spawn a `Future` as a `Task` on the current `Reactor`. | ||
| /// | ||
| /// Panics if called from outside `block_on`. | ||
| #[cfg(target_env = "p2")] | ||
| pub fn spawn<F, T>(fut: F) -> Task<T> | ||
| where | ||
| F: std::future::Future<Output = T> + 'static, | ||
| T: 'static, | ||
| { | ||
| Reactor::current().spawn(fut) | ||
| } | ||
|
|
||
| #[cfg(target_env = "p3")] | ||
| pub use ::async_task::Runnable; | ||
| #[cfg(target_env = "p3")] | ||
| pub use wasip3::wit_bindgen::block_on; | ||
|
|
||
| /// Spawn a `Future` as a `Task` on the WASI 0.3 async runtime. | ||
| #[cfg(target_env = "p3")] | ||
| pub fn spawn<F, T>(fut: F) -> Task<T> | ||
| where | ||
| F: std::future::Future<Output = T> + 'static, | ||
| T: 'static, | ||
| { | ||
| let (runnable, task) = async_task::spawn_local(fut, |runnable: Runnable| { | ||
| // Scheduling the task is accomplished by spawning a future which | ||
| // executes the `run` method. | ||
| wasip3::spawn_local(async move { | ||
| let _ = runnable.run(); | ||
| }); | ||
| }); | ||
| runnable.schedule(); | ||
| task | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| use std::cell::Cell; | ||
| use std::future::pending; | ||
| use std::rc::Rc; | ||
|
|
||
| use futures_lite::future::yield_now; | ||
|
|
||
| struct DropFlag(Rc<Cell<bool>>); | ||
|
|
||
| impl Drop for DropFlag { | ||
| fn drop(&mut self) { | ||
| self.0.set(true); | ||
| } | ||
| } | ||
|
|
||
| async fn wait_until(flag: &Cell<bool>) { | ||
| while !flag.get() { | ||
| yield_now().await; | ||
| } | ||
| } | ||
|
|
||
| #[wstd::test] | ||
| async fn spawn_detach_cancel_and_drop() { | ||
| assert_eq!(wstd::runtime::spawn(async { 42 }).await, 42); | ||
|
|
||
| // Check that a detached task completes eventually. | ||
| let detached_completed = Rc::new(Cell::new(false)); | ||
| let completed = detached_completed.clone(); | ||
| wstd::runtime::spawn(async move { | ||
| yield_now().await; | ||
| completed.set(true); | ||
| }) | ||
| .detach(); | ||
|
|
||
| assert!(!detached_completed.get()); | ||
| wait_until(&detached_completed).await; | ||
| assert!(detached_completed.get()); | ||
|
|
||
| // Check that calling `cancel` on a task cancels and drops the spawned | ||
| // future. | ||
| let canceled_started = Rc::new(Cell::new(false)); | ||
| let canceled_dropped = Rc::new(Cell::new(false)); | ||
| let canceled_completed = Rc::new(Cell::new(false)); | ||
| let started = canceled_started.clone(); | ||
| let dropped = canceled_dropped.clone(); | ||
| let completed = canceled_completed.clone(); | ||
| let task = wstd::runtime::spawn(async move { | ||
| let _drop_flag = DropFlag(dropped); | ||
| started.set(true); | ||
| pending::<()>().await; | ||
| completed.set(true); | ||
| }); | ||
|
|
||
| wait_until(&canceled_started).await; | ||
| assert_eq!(task.cancel().await, None); | ||
| assert!(canceled_dropped.get()); | ||
| assert!(!canceled_completed.get()); | ||
|
|
||
| // Check that dropping a task cancels and drops the spawned future. | ||
| let dropped_started = Rc::new(Cell::new(false)); | ||
| let dropped_dropped = Rc::new(Cell::new(false)); | ||
| let dropped_completed = Rc::new(Cell::new(false)); | ||
| let started = dropped_started.clone(); | ||
| let dropped = dropped_dropped.clone(); | ||
| let completed = dropped_completed.clone(); | ||
| let task = wstd::runtime::spawn(async move { | ||
| let _drop_flag = DropFlag(dropped); | ||
| started.set(true); | ||
| pending::<()>().await; | ||
| completed.set(true); | ||
| }); | ||
|
|
||
| wait_until(&dropped_started).await; | ||
| drop(task); | ||
| wait_until(&dropped_dropped).await; | ||
| assert!(!dropped_completed.get()); | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little disappointed that
wasip3::spawn_localcan't return a handle, and I haven't explored why we can't add that in wit-bindgen, but I assume thats a problem too big to solve right now. My biggest concern here is that if we're relying on the CM task's context to take care of disambiguating context at the host (i.e. the CDN-Loop problem), we need the not-yet-existing guest functionality for getting and setting task context.I guess we have to roll with this because its the only way for now? But lets also make sure we follow up on the task context set/get soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made the change to have
spawn_localreturn a handle already here: bytecodealliance/wit-bindgen#1710 (comment) , so we can switch to that in a follow up after bothwit-bindgenandwasip3are released. But yes we should also still follow up on the context get/set separately.