-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Implement support for async
functions in Wasmtime
#2434
Conversation
Subscribe to Label Actioncc @peterhuene
This issue or pull request has been labeled: "wasmtime:api"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
Instantiation right now uses a recursive `instantiate` function since it was relatively easy to write that way, but this is unfortunately not factored in a way friendly to the async implementation in bytecodealliance#2434. This commit refactors the function to instead use an iterative loop and refactors code in such a way that it should be easy to rebase bytecodealliance#2434 on top of this change. The main goal is to make the body of `Instance::new` as small as possible since it needs to be duplicated with `Instance::new_async`.
Instantiation right now uses a recursive `instantiate` function since it was relatively easy to write that way, but this is unfortunately not factored in a way friendly to the async implementation in bytecodealliance#2434. This commit refactors the function to instead use an iterative loop and refactors code in such a way that it should be easy to rebase bytecodealliance#2434 on top of this change. The main goal is to make the body of `Instance::new` as small as possible since it needs to be duplicated with `Instance::new_async`.
Instantiation right now uses a recursive `instantiate` function since it was relatively easy to write that way, but this is unfortunately not factored in a way friendly to the async implementation in bytecodealliance#2434. This commit refactors the function to instead use an iterative loop and refactors code in such a way that it should be easy to rebase bytecodealliance#2434 on top of this change. The main goal is to make the body of `Instance::new` as small as possible since it needs to be duplicated with `Instance::new_async`.
Instantiation right now uses a recursive `instantiate` function since it was relatively easy to write that way, but this is unfortunately not factored in a way friendly to the async implementation in #2434. This commit refactors the function to instead use an iterative loop and refactors code in such a way that it should be easy to rebase #2434 on top of this change. The main goal is to make the body of `Instance::new` as small as possible since it needs to be duplicated with `Instance::new_async`.
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.
(Since I'm commenting anyway, figured I'd say I'm excited for this feature. I've been using your WIP fiber crate to wrap the released Wasmtime and experiment with running Wasm asynchronously. So far it's working great on Mac OS and Linux x86_64)
1e398ca
to
2742bcd
Compare
d268d6c
to
2ecaf08
Compare
This is an implementation of [RFC 2] in Wasmtime which is to support `async`-defined host functions. At a high level support is added by executing WebAssembly code that might invoke an asynchronous host function on a separate native stack. When the host function's future is not ready we switch back to the main native stack to continue execution. There's a whole bunch of details in this commit, and it's a bit much to go over them all here in this commit message. The most important changes here are: * A new `wasmtime-fiber` crate has been written to manage the low-level details of stack-switching. Unixes use `mmap` to allocate a stack and Windows uses the native fibers implementation. We'll surely want to refactor this to move stack allocation elsewhere in the future. Fibers are intended to be relatively general with a lot of type paremters to fling values back and forth across suspension points. The whole crate is a giant wad of `unsafe` unfortunately and involves handwritten assembly with custom dwarf CFI directives to boot. Definitely deserves a close eye in review! * The `Store` type has two new methods -- `block_on` and `on_fiber` which bridge between the async and non-async worlds. Lots of unsafe fiddly bits here as we're trying to communicate context pointers between disparate portions of the code. Extra eyes and care in review is greatly appreciated. * The APIs for binding `async` functions are unfortunately pretty ugly in `Func`. This is mostly due to language limitations and compiler bugs (I believe) in Rust. Instead of `Func::wrap` we have a `Func::wrapN_async` family of methods, and we've also got a whole bunch of `Func::getN_async` methods now too. It may be worth rethinking the API of `Func` to try to make the documentation page actually grok'able. This isn't super heavily tested but the various test should suffice for engaging hopefully nearly all the infrastructure in one form or another. This is just the start though! [RFC 2]: bytecodealliance/rfcs#2
This commit implements APIs on `Store` to periodically yield execution of futures through the consumption of fuel. When fuel runs out a future's execution is yielded back to the caller, and then upon resumption fuel is re-injected. The goal of this is to allow cooperative multi-tasking with futures.
Turns out this is another caller-saved register!
Take a leaf out of aarch64's playbook and don't have extra memory to load/store these arguments, instead leverage how `wasmtime_fiber_switch` already loads a bunch of data into registers which we can then immediately start using on a fiber's start without any extra memory accesses.
With fuel it's probably best to not provide any way to inject infinite fuel.
* Use a shared header file to deduplicate some directives * Guarantee hidden visibility for functions * Enable gc-sections on macOS x86_64 * Add `.type` annotations for ARM
.flat_map(|f| f.symbols()) | ||
.filter_map(|s| Some(s.name()?.to_string())) | ||
.any(|s| s.contains("look_for_me")) | ||
// TODO: apparently windows unwind routines don't unwind through fibers, so this will always fail. Is there a way we can fix that? |
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'll ping some Microsoft debugger folks to see if it's a known limitation of StackWalkEx
when the given thread is a fiber. I suspect RtlVirtualUnwind
might not have this problem.
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.
Just those few comments above. This looks fantastic and I'm excited to see what gets built on top of this feature.
This is an implementation of RFC 2 in Wasmtime which is to support
async
-defined host functions. At a high level support is added byexecuting WebAssembly code that might invoke an asynchronous host
function on a separate native stack. When the host function's future is
not ready we switch back to the main native stack to continue execution.
There's a whole bunch of details in this commit, and it's a bit much to
go over them all here in this commit message. The most important changes
here are:
A new
wasmtime-fiber
crate has been written to manage the low-leveldetails of stack-switching. Unixes use
mmap
to allocate a stack andWindows uses the native fibers implementation. We'll surely want to
refactor this to move stack allocation elsewhere in the future. Fibers
are intended to be relatively general with a lot of type paremters to
fling values back and forth across suspension points. The whole crate
is a giant wad of
unsafe
unfortunately and involves handwrittenassembly with custom dwarf CFI directives to boot. Definitely deserves
a close eye in review!
The
Store
type has two new methods --block_on
andon_fiber
which bridge between the async and non-async worlds. Lots of unsafe
fiddly bits here as we're trying to communicate context pointers
between disparate portions of the code. Extra eyes and care in review
is greatly appreciated.
The APIs for binding
async
functions are unfortunately pretty uglyin
Func
. This is mostly due to language limitations and compilerbugs (I believe) in Rust. Instead of
Func::wrap
we have aFunc::wrapN_async
family of methods, and we've also got a wholebunch of
Func::getN_async
methods now too. It may be worthrethinking the API of
Func
to try to make the documentation pageactually grok'able.
This isn't super heavily tested but the various test should suffice for
engaging hopefully nearly all the infrastructure in one form or another.
This is just the start though!