Skip to content

Commit

Permalink
Define how start+async work
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewagner committed Jan 20, 2025
1 parent 49f1732 commit ed9a79c
Showing 1 changed file with 45 additions and 13 deletions.
58 changes: 45 additions & 13 deletions design/mvp/Async.md
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,47 @@ return values from `task.wait` in the previous example. The precise meaning of
these values is defined by the Canonical ABI.


## Interaction with the start function

Since any component-level function with an empty signature can be used as a
[`start`] function, there's nothing to stop an `async`-lifted function from
being a `start` function. Async start functions are useful in scenarios like
the following:
* If the top-level scripts of a scripting language are executed by the `start`
function, asychrony arises from regular use of the language's concurrency
features. For example, in JS, this takes the form of [top-level `await`].
* If C++ or other OOPLs global object constructors are executed by the `start`
function, these can execute general-purpose code which may need to perform
concurrent I/O.

Since component `start` definitions are already specified to make synchronous
function calls that must return before the next `start` definition executes or
and before the component instance is considered initialized (and ready for
export calls), the natural extension to `async`-lifted functions is for `start`
to wait until the `async` callee [returns](#returning), just like a synchronous
`canon lower`. This gives `async` `start` functions a simple way to do
concurrent work and signal completion using the same language bindings as
regular `async` `export` functions.

As explained above, an async task can always continue executing after reaching
the "returned" state and thus an `async` task spawned by a `start` definition
may continue executing even after it has returned and the component instance is
considered fully initialized. These post-return tasks can be used by the
language toolchain to implement traditional "background tasks" (e.g., the
`setInterval()` or `requestIdleCallback()` JavaScript APIs). From the
perspective of [structured concurrency], these background tasks are new task
tree roots, sibling to the roots created when component exports are called by
the host. Thus, subtasks (and, later, threads) spawned by the background task
will have proper async callstacks as used to define reentrancy and for
debugging/profiling.

In future, when [runtime instantiation] is added to the Component Model, the
component-level function used to create a component instance could be lowered
with `async` to allow a parent component instance to do instantiation of child
components concurrently with other tasks, relaxing the fully synchronous
model of instantiation presented above.


## Interaction with multi-threading

For now, the integration between multi-threading (via [`thread.spawn`]) and
Expand Down Expand Up @@ -580,16 +621,6 @@ it does present interesting opportunities to experiment with optimizations over
time as applications are built with more components.


## Interaction with the start function

Since component-level start functions can be any component-level function (with
type `[] -> []`), async functions can be start functions. This raises some
interesting questions concerning how much concurrency during instantiation (of
a whole component DAG) is allowed and how parent components can control this.
For now, this remains a [TODO](#todo) and validation will reject `async`-lifted
`start` functions.


## TODO

Native async support is being proposed incrementally. The following features
Expand All @@ -598,8 +629,6 @@ will be added in future chunks roughly in the order list to complete the full
comes after:
* `nonblocking` function type attribute: allow a function to declare in its
type that it will not transitively do anything blocking
* define what `async` means for `start` functions (top-level await + background
tasks), along with cross-task coordination built-ins
* `subtask.cancel`: allow a supertask to signal to a subtask that its result is
no longer wanted and to please wrap it up promptly
* zero-copy forwarding/splicing
Expand Down Expand Up @@ -653,10 +682,13 @@ comes after:
[Use Cases]: ../high-level/UseCases.md
[Blast Zone]: FutureFeatures.md#blast-zones
[Reentrance]: Explainer.md#component-invariants
[`start`]: Explainer.md#start-definitions

[stack-switching]: https://github.com/WebAssembly/stack-switching/
[JSPI]: https://github.com/WebAssembly/js-promise-integration/
[shared-everything-threads]: https://github.com/webAssembly/shared-everything-threads

[WASI Preview 3]: https://github.com/WebAssembly/WASI/tree/main/wasip2#looking-forward-to-preview-3
[`wasi:http/handler.handle`]: https://github.com/WebAssembly/wasi-http/blob/main/wit-0.3.0-draft/handler.wit
[Runtime Instantiation]: https://github.com/WebAssembly/component-model/issues/423

[Top-level `await`]: https://github.com/tc39/proposal-top-level-await

0 comments on commit ed9a79c

Please sign in to comment.