-
Notifications
You must be signed in to change notification settings - Fork 24
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
RFC: Add async
support to wasmtime
#2
RFC: Add async
support to wasmtime
#2
Conversation
This RFC proposes adding native support to the `wasmtime` crate for imported functions which are `async`. Additionally it provides APIs to call wasm functions as an `async` function.
ty: FuncType, | ||
state: T, | ||
func: fn(Caller<'a>, &'a T, &'a [Val], &'a mut [Val]) -> R, | ||
) -> Func |
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.
Should this (and wrap_async
) be -> Result<Func>
so that it can gracefully fail when given a non-async Store
?
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.
Erm, should have kept reading to where you talk about panicking. Not sure I like the idea of panicking for the mismatch, but I can see the logic in doing so.
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.
It's true yeah that either way is technically possible, but I would personally tend to classify this as a programmer error because applications would be expected to pick one or the other of async or not, and if you get it wrong by accident it seems like you'd want to discover that ASAP
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 onboard with panicking 👍
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 agree with panicking on mismatch, if async is an internal implementation detail of the Store
.
Is there any way, though, that we could capture this in the type system, and then produce a type error at compile time?
I can think of two ways to do that. (All names below are drafts, intended as placeholders; we can always change them.)
-
We could make
Store
take a type parameter for a marker type, where most functions acceptStore<A>
for anyA
, but functions that require an async-capable store requireStore<AsyncStore>
. MostStore
functions would be implemented forStore<A>
for anyA
, butStore::new
andStore::new_async
would return one of the two specific types. Then, if you attempt to callFunc::new_async
with aStore<SyncStore>
you'll get a compile error. This has two advantages:Store
is the same type with no duplicate implementation effort, and the async property becomes a compile-time property rather than a run-time boolean. Most code will only ever use one of those two types, so this should not add to code size; any given program should only monomorphize one of those two types. The disadvantage would be having to pass around the generic parameter, but that seems worth getting this checked at compile time. We could also have type aliases for the two types. -
We could have two types,
Store
andStoreAsync
, and implement a sealed traitAnyStore
on them (so that those are the only two instances). Most functions would acceptimpl AnyStore
, and functions likeFunc::new_async
would accept onlyStoreAsync
specifically. This has the advantage of not requiring a generic parameter everywhere. The disadvantage is that we'd need a more complex set of forwarding functions within wasmtime to handle the two distinct types, so while we could share one implementation, we'll have 2-3 sets of function signatures and simple forwarding stubs for many of the same functions.
Personally, I'd favor the former approach: Store<SyncStore>
, Store<AsyncStore>
, and type aliases for those two so people don't normally have to think about the generic parameter.
Thoughts?
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 it's possible to move this into the type system but I'd fear that we're kind of shooting ourselves in the foot too much and/or sacrificing ergonomics. We would not only need an async store type-level distinction but we would also need and async function type-level distinction because if you extract a function from an instance then you can only call it asynchronously in an async store. But if we don't want a runtime footgun then we'd also need async instances, and this quickly spirals to creating almost double the api surface area.
Overall it seemed that given the use cases in mind a runtime flag would be fine here and errors would be weeded out very quickly at runtime instead of surprising someone in production by accident.
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.
@alexcrichton Ah, I see. Having to add that parameter to many different types does indeed make this much less appealing.
One other alternative: could we consider a wasmtime feature flag for this, enabling/disabling async support, and then if it's enabled, we automatically use it for everything?
A concrete use case in my mind that's motivating the attempt to make this more than just a runtime flag: suppose you're writing a library crate that works with wasmtime and you provide functions to be called with wasmtime types, but you're not the top-level crate that's instantiating wasmtime. For instance, you want to write a library crate to expose some interesting functions, or to manage some objects. Conversely, the top-level crate may not know if it needs to call new
or new_async
, because it doesn't need any async itself but its dependencies might. If your crate requires async support, it seems unfortunate if that only lives in your crate's documentation, and you have to tell people "make sure to use new_async
or you'll get runtime panics". I'd really love for the crate to be able to express, whether through the type system or through a cargo feature, "I need async and won't work without it".
If that's not possible, I understand. But if there's any way we can let crates surface "I need an async runtime" programmatically and ensure that they get one, rather than just adding it to their README, I think that'd be a usability improvement.
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 definitely agree that the scenario you're outlining is a downside. Unfortunately though I don't know of a fix other than two crates, wasmtime
and wasmtime-async
. Switching between async/not-async isn't really a good fit for Cargo features which are additive (but this is more mutually exclusive).
The only real way for embedders to support this in libraries would be to either pick one or to also support both async and sync APIs.
Put another way, I think a runtime flag is a local optimum for "don't duplicate the API surface area" as a constraint. Otherwise there are possibilities like different types or different crates I think, although I'm not sure which is best myself.
This looks great and I'm really excited for this! I think what is outlined in this RFC makes sense and I don't have concerns other than how tricky implementing a feature like this will be. I'm also in the process of writing an RFC for "instance pools" where we can reserve all of an instance's resources upfront (with configurable constraints since limits will need to be enforced) in a module-agnostic manner to allow for extremely fast instance creation in highly-concurrent scenarios à la Lucet. Part of that work will include setting up a region in virtual memory that can be used for the alternative stack for each instance and thus will have some overlap with this proposal. |
Heh yeah I figured that the work you're doing would lend itself well to stack allocation, so seems reasonable to defer to that! |
Because we are never spawning futures ourselves, and because leaf I guess if |
Right yeah, that's a goal of this integration is that it's all executor-agnostic. Wasmtime won't necessarily be a leaf future but we'll always be executing within the context of a future's |
Ok I've been implementing this and I've run into a snag so far which is somewhat unfortunate. This proposed signature: impl Func {
pub fn new_async<'a, T, R>(
store: &Store,
ty: FuncType,
state: T,
func: fn(Caller<'a>, &'a T, &'a [Val], &'a mut [Val]) -> R,
) -> Func
where
R: Future<Output = Result<(), Trap>> + 'a,
{
// ...
}
} does not compile in that it's not what we want. The problem with this signature is that it's letting the caller pick async fn foo(caller: Caller<'static>, state: &'static i32, params: &'static [Val], results: &'static [Val]) -> Result<(), Trap> {
// ...
}
Func::new_async(store, ty, 0i32, |a, b, c, d| async { foo(a, b, c, d).await }) Clearly Unfortunately though when using a HRTB I can't figure out how to return an unboxed future. This means that the final signature I'm working with is: pub fn new_async<T, R, F>(store: &Store, ty: FuncType, state: T, func: F) -> Func
where
F: for<'a> Fn(
Caller<'a>,
&'a T,
&'a [Val],
&'a mut [Val],
) -> Box<dyn Future<Output = Result<(), Trap>> + 'a>
+ 'static,
T: 'static, which... wow is that a mouthful. It expresses what we want though in that as the callee we pick Func::wrap_async(store, state, |state, param| Box::new(async { state.async_method(param) })) which... is also a mouthful. Unfortunately though I can't really figure out how to make this more ergonomic. I basically don't know how to write a function that takes an async function. I don't think that Rust-the-language really has what we want at this time in that this is just a fact of life when working with async, but if others have suggestions on how to improve this they would be most welcome! |
I think this works for making the future not a boxed trait object: pub fn new_async<T, R, Fut, F>(store: &Store, ty: FuncType, state: T, func: F) -> Func
where
F: for<'a> Fn(
Caller<'a>,
&'a T,
&'a [Val],
&'a mut [Val],
) -> Fut
+ 'static,
T: 'static,
Fut: Future<Output = ()>
{
todo!()
} Or am I missing something? I haven't actually tried calling it with anything so it might be one of those rare signatures that type checks alone, but doesn't actually work at any call sites. |
Ah yeah that will indeed compile, but the problem is that the future returned can't borrow any of the input arguments with that signature or the input state itself. That means that it won't be compatible with most This is where I think it's not possible in Rust, what we really want is |
Yeah you would need to put closed-over state into |
If you can't close over the arguments, however, I fear that it loses a key bit of expressivity with async functions. One consequence, for example, is that for let my_state = the_rc.clone();
let func = Func::wrap_async(&store, move || {
let futures_state = my_state.clone();
async move { futures_state.async_method().await }
}); compared with, if you can close over state: let my_state = the_rc.clone();
let func = Func::wrap_async(&store, my_state, move |state| {
Box::new(async move { state.async_method().await })
}); Overall I think there's not a great answer here. Nothing feels "obviously right" because the |
lol good point yeah seems like a boxed trait object is the way to go until some future time when Rust gets more expressive. |
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
Ok I've posted the initial implementation I've got for this. The only API change other than what I already mentioned above is that Overall I'm really not all that happy with how the API turn out in terms of ergonomics, but I am pretty happy with where it is in terms of expressivity and what you can do with it. |
I posted a comment above about checking for sync/async stores via the type system at compile time, rather than panicking at runtime. I'm also wondering: will this be forward-compatible with wasm being able to call host functions with a promise-like interface? Right now, the current proposal still looks like a blocking call to wasm. Eventually, I'd like wasm to be able to ask the host to start a computation and return a promise, and later ask for the value from the promise, while doing other work in the interim (including calling other host functions that return promises, as well). |
AFAIK there's not any proposals right now for wasm calling a host function and getting a promise of some kind. In that sense it's hard to say. Otherwise to do what you're thinking today I believe that would need host-layer emulation where you'd return handles back to wasm and wasm could query the status of the handle later on. |
On Mon, Nov 23, 2020 at 07:26:25AM -0800, Alex Crichton wrote:
I definitely agree that the scenario you're outlining is a downside. Unfortunately though I don't know of a fix other than two crates, `wasmtime` and `wasmtime-async`. Switching between async/not-async isn't really a good fit for Cargo features which are additive (but this is more mutually exclusive).
From the RFC, it sounded like async wasmtime structures could support
synchronous functions, it's just that synchronous-only wasmtime
structures can't support asynchronous functions. Is that the case? That
*seems* like the right use case for Cargo features: if anyone needs
async, wasmtime gets built with async support, which may add overhead
for the sync case.
|
@joshtriplett oh that's right for defining functions but calling functions must match the type of store. As a library you could always define a synchronous host function, but for calling wasm you're always going to need to match the store which is where the mutual exclusivity comes into play. |
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
Motion to finalize with a disposition to mergeI'm proposing that we merge this RFC. Early feedback has been received and all requested outstanding changes to the proposal has been made. An implementation in Wasmtime has already been started. Stakeholders sign-offTagging all employees of BA-affiliated companies who have committed to the Wasmtime repo in the last three months plus anyone who has given feedback on this PR as a stakeholder. FastlyIBMIntelMozilla |
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.
✔️
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.
🎉
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 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 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 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 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 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 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 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
Entering Final Call Period
The FCP will end on February 26th. |
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 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 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
The FCP has elapsed without any objections being raised. I'm going to merge this. Thanks everybody! |
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
* Implement support for `async` functions in Wasmtime 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 * Add wasmtime-fiber to publish script * Save vector/float registers on ARM too. * Fix a typo * Update lock file * Implement periodically yielding with fuel consumption 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. * Fix compile without async * Save/restore the frame pointer in fiber switching Turns out this is another caller-saved register! * Simplify x86_64 fiber asm 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. * Add x86 support to wasmtime-fiber * Add ARM32 support to fiber crate * Make fiber build file probing more flexible * Use CreateFiberEx on Windows * Remove a stray no-longer-used trait declaration * Don't reach into `Caller` internals * Tweak async fuel to eventually run out. With fuel it's probably best to not provide any way to inject infinite fuel. * Fix some typos * Cleanup asm a bit * 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 * Update lock file * Fix compile error * Review comments
This RFC proposes adding native support to the
wasmtime
crate forimported functions which are
async
. Additionally it provides APIs tocall wasm functions as an
async
function.rendered