Skip to content
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

Add support for async/streams/futures #9582

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

dicej
Copy link
Contributor

@dicej dicej commented Nov 7, 2024

This adds support for loading, compiling, linking, and running components which use the Async ABI along with the stream, future, and error-context types. It also adds support for generating host bindings such that multiple host functions can be run concurrently with guest tasks -- without monopolizing the Store.

See the implementation RFC for details, as well as this repo containing end-to-end smoke tests.

This is very much a work-in progress, with a number of tasks remaining:

  • Avoid exposing global task IDs to guests and use per-instance IDs instead (and fix no_std build at the same time)
  • Track task.return type during compilation and assert the actual and expected types match at runtime
  • Implement {stream|future}.cancel-{read|write}
  • Add support for (Typed)Func::call_concurrent per the RFC
  • Add support for multiplexing stream/future reads/writes and concurrent calls to guest exports per the RFC
  • Add support for dynamic APIs for async/streams/futures/error-contexts
  • Add support for callback-less async functions (e.g. goroutines)
  • Add end-to-end tests covering new functionality (i.e. port over the tests in https://github.com/dicej/component-async-demo)
  • Add WAT-based tests covering new functionality to tests/all/component_model
  • Add binding generation test cases to crates/component-macro/tests
  • Add WAST tests to tests/misc_testsuite/component-model
  • Add doc and code comments to new, non-trivial code (especially concurrent.rs and futures_and_streams.rs)
  • Implement error-context.{new|debug-string}
  • Ensure all guest pointers are bounds-checked when lifting, lowering, or copying values
  • Guard against reentrance where required (e.g. in certain fused adapter calls)
  • Call any post_return functions after lifting results
  • Minimize and/or document the use of unsafe code
  • Refactor, clean up, and unify handling of backpressure, yields, and event polling
  • Reduce code duplication in wasmtime_cranelift::compiler::component
  • Reduce code duplication between StoreContextMut::on_fiber and concurrent::on_fiber
  • Switch back to upstream wasm-tools once Add support for async ABI, futures, streams, and errors wasm-tools#1895 has been merged and released
  • Switch back to upstream wit-bindgen once Add support for async/streams/futures to Rust generator wit-bindgen#1082 has been merged and released

This adds support for loading, compiling, linking, and running components which
use the [Async
ABI](https://github.com/WebAssembly/component-model/blob/main/design/mvp/Async.md)
along with the [`stream`, `future`, and
`error-context`](WebAssembly/component-model#405) types.
It also adds support for generating host bindings such that multiple host
functions can be run concurrently with guest tasks -- without monopolizing the
`Store`.

See the [implementation RFC](bytecodealliance/rfcs#38)
for details, as well as [this
repo](https://github.com/dicej/component-async-demo) containing end-to-end smoke
tests.

Signed-off-by: Joel Dice <[email protected]>

fix clippy warnings and bench/fuzzing errors

Signed-off-by: Joel Dice <[email protected]>

revert atomic.wit whitespace change

Signed-off-by: Joel Dice <[email protected]>

fix build when component-model disabled

Signed-off-by: Joel Dice <[email protected]>

bless component-macro expected output

Signed-off-by: Joel Dice <[email protected]>

fix no-std build error

Signed-off-by: Joel Dice <[email protected]>

fix build with --no-default-features --features runtime,component-model

Signed-off-by: Joel Dice <[email protected]>

partly fix no-std build

It's still broken due to the use of `std::collections::HashMap` in
crates/wasmtime/src/runtime/vm/component.rs.  I'll address that as part of the
work to avoid exposing global task/future/stream/error-context handles to
guests.

Signed-off-by: Joel Dice <[email protected]>

maintain per-instance tables for futures, streams, and error-contexts

Signed-off-by: Joel Dice <[email protected]>

refactor task/stream/future handle lifting/lowering

This addresses a couple of issues:

- Previously, we were passing task/stream/future/error-context reps directly to
  instances while keeping track of which instance had access to which rep.  That
  worked fine in that there was no way to forge access to inaccessible reps, but
  it leaked information about what other instances were doing.  Now we maintain
  per-instance waitable and error-context tables which map the reps to and from
  the handles which the instance sees.

- The `no_std` build was broken due to use of `HashMap` in
  `runtime::vm::component`, which is now fixed.

Note that we use one single table per instance for all tasks, streams, and
futures.  This is partly necessary because, when async events are delivered to
the guest, it wouldn't have enough context to know which stream or future we're
talking about if each unique stream and future type had its own table.  So at
minimum, we need to use the same table for all streams (regardless of payload
type), and likewise for futures.  Also, per
WebAssembly/component-model#395 (comment),
the plan is to move towards a shared table for all resource types as well, so
this moves us in that direction.

Signed-off-by: Joel Dice <[email protected]>

fix wave breakage due to new stream/future/error-context types

Signed-off-by: Joel Dice <[email protected]>

switch wasm-tools to v1.220.0-based branch

Signed-off-by: Joel Dice <[email protected]>

check `task.return` type at runtime

We can't statically verify a given call to `task.return` corresponds to the
expected core signature appropriate for the currently running task, so we must
do so at runtime.  In order to make that check efficient, we intern the types.

My initial plan was to use `ModuleInternedTypeIndex` and/or `VMSharedTypeIndex`
for interning, but that got hairy with WasmGC considerations, so instead I added
new fields to `ComponentTypes` and `ComponentTypesBuilder`.

Signed-off-by: Joel Dice <[email protected]>

add `TypedFunc::call_concurrent` and refine stream/future APIs

This implements what I proposed in https://github.com/dicej/rfcs/blob/component-async/accepted/component-model-async.md#wasmtime.  Specifically, it adds:

- A new `Promise` type, useful for working with concurrent operations that require access to a `Store` to make progress.
- A new `PromisesUnordered` type for `await`ing multiple promises concurrently
-`TypedFunc::call_concurrent` (which returns a `Promise`), allowing multiple host->guest calls to run concurrently on the same instance.
- Updated `{Stream|Future}{Writer|Reader}` APIs which use `Promise`

The upshot is that the embedder can now ergonomically manage arbitrary numbers
of concurrent operations.  Previously, this was a lot more difficult to do
without accidentally starving some of the operations due to another one
monopolizing the `Store`.

Finally, this includes various refactorings and fixes for bugs exposed by the
newer, more versatile APIs.

Signed-off-by: Joel Dice <[email protected]>

clean up verbosity in component/func.rs

Signed-off-by: Joel Dice <[email protected]>

snapshot

Signed-off-by: Joel Dice <[email protected]>

implement stream/future read/write cancellation

This required a somewhat viral addition of `Send` and `Sync` bounds for async
host function closure types, unfortunately.

Signed-off-by: Joel Dice <[email protected]>

add `Func::call_concurrent` and `LinkerInstance::func_new_concurrent`

Signed-off-by: Joel Dice <[email protected]>

dynamic API support for streams/futures/error-contexts

Signed-off-by: Joel Dice <[email protected]>

support callback-less (AKA stackful) async lifts

Signed-off-by: Joel Dice <[email protected]>

fix `call_host` regression

Signed-off-by: Joel Dice <[email protected]>

add component model async end-to-end tests

I've ported these over from https://github.com/dicej/component-async-demo

Signed-off-by: Joel Dice <[email protected]>

fix test regressions and clippy warnings

Signed-off-by: Joel Dice <[email protected]>

satisfy clippy

Signed-off-by: Joel Dice <[email protected]>
Enabling this feature for all tests revealed various missing pieces in the new
`concurrent.rs` fiber mechanism, which I've addressed.

This adds a bunch of ugly `#[cfg(feature = "component-model-async")]` guards,
but those will all go away once I unify the two async fiber implementations.

Signed-off-by: Joel Dice <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

1 participant