-
Notifications
You must be signed in to change notification settings - Fork 922
Async execution API + concurrent execution support, take 2 #5920
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
base: main
Are you sure you want to change the base?
Conversation
* Add proper trap code for suspending in non-async context
…structors are not guaranteed to run
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.
Pull request overview
This PR introduces comprehensive support for async execution and concurrent function calls in Wasmer. It implements a JSPI-like (JavaScript Promise Integration) async API that allows WebAssembly functions to suspend and resume execution using coroutines, enabling proper integration with async Rust ecosystems.
Key changes:
- Adds
StoreAsynctype and async function APIs (Function::call_async,Function::new_async, etc.) - Implements a custom single-threaded
LocalRwLockfor efficient async-aware locking without atomics - Introduces coroutine-based async runtime using
corosenseifor suspending/resuming WASM execution - Adds comprehensive test coverage for async operations and greenthread-style context switching
Reviewed changes
Copilot reviewed 41 out of 42 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
lib/api/src/entities/store/async_.rs |
Defines StoreAsync, AsStoreAsync trait, and async lock types for concurrent store access |
lib/api/src/entities/store/local_rwlock.rs |
Custom single-threaded async-aware RwLock implementation (829 lines with comprehensive tests) |
lib/api/src/entities/store/context.rs |
Thread-local store context stack management for tracking active stores during execution |
lib/api/src/backend/sys/async_runtime.rs |
Coroutine-based async runtime for suspending/resuming WASM functions with host futures |
lib/api/src/entities/function/mod.rs |
Adds call_async, new_async, new_typed_async APIs and related methods |
lib/api/src/entities/function/async_host.rs |
Defines AsyncHostFunction trait and environment wrappers for async imports |
lib/api/src/backend/sys/entities/function/mod.rs |
Core implementation of async function trampolines and host call handling |
lib/api/tests/jspi_async.rs |
Comprehensive async tests including state management, typed functions, and multiple active coroutines |
lib/api/tests/simple_greenthread.rs |
Green thread-style context switching tests with async spawning |
lib/types/src/trapcode.rs |
Adds YieldOutsideAsyncContext trap code for error handling |
lib/vm/src/trap/traphandlers.rs |
Adds Clone derive to VMConfig |
tests/wasix/vfork/main.c |
Replaces function pointer trap with __builtin_trap() |
| Various documentation files | Updates examples to use store.engine() and store.as_mut() patterns |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| self.0.data_and_store_mut() | ||
| } | ||
|
|
||
| /// Creates an [`AsStoreAsync`] from this [`AsyncFunctionEnvMut`] if the current |
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.
| /// Creates an [`AsStoreAsync`] from this [`AsyncFunctionEnvMut`] if the current | |
| /// Creates an [`AsStoreAsync`] from this [`FunctionEnvMut`] if the current |
|
Being able to upgrade a let (data, mut store) = ctx.data_and_store_mut();
... some sync stuff
let mut async_store = ctx.as_store_async().unwrap();
... do something with the async store
... do more sync stuff with storedoes not compile as I can't access |
| pub(crate) id: StoreId, | ||
| pub(crate) inner: LocalRwLock<StoreInner>, |
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.
Having these fields acccesible outside of this module makes a safe implementation (and review) of StoreAsync much more complex, as we need to consider all uses to ensure they don't change. Also, it makes it possible to construct new `StoreAsyncs outside this module.
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.
A lot of the runtime code does the same thing where, instead of creating a huge number of functions, we just expose a type's internals to the entire crate. I don't see a reason why StoreAsync should be different.
lib/api/src/entities/store/async_.rs
Outdated
| pub(crate) _marker: PhantomData<&'a ()>, | ||
| } | ||
|
|
||
| impl<'a> AsyncStoreReadLock<'a> { |
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 think this should be named AsyncStoreReadGuard, because that's usually the pattern for the name of guards in RwLock
lib/api/src/entities/store/async_.rs
Outdated
| } | ||
| } | ||
|
|
||
| pub(crate) enum AsyncStoreReadLockInner { |
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.
Use either StoreAsync or AsyncStore consistently
| impl StoreAsync { | ||
| /// Transform this [`StoreAsync`] back into a [`Store`] | ||
| /// if this is the only clone of it and is unlocked. | ||
| pub fn into_store(self) -> Result<Store, Self> { |
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.
The opposite is called into_async so I was somehow expecting this to be called into_sync
| pub(crate) inner: LocalRwLock<StoreInner>, | ||
| } | ||
|
|
||
| impl StoreAsync { |
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.
StoreAsync does not implement Debug, so I can not unwrap the result of into_store.
* make the call_async future 'static * Eliminate the ugly and now-impossible multiple active coroutines test in favor of the greenthreads test
* Remove lifetime bound on AsyncFunctionEnvHandle and friends, since it's a properly owned type now
Supersedes #5906. WIP as of now.