@@ -71,6 +71,7 @@ the same way that they already bind to various OS's concurrent I/O APIs (such
7171as ` select ` , ` epoll ` , ` io_uring ` , ` kqueue ` and Overlapped I/O) making the
7272Component Model "just another OS" from the language toolchain's perspective.
7373
74+ TODO
7475The new async ABI can be used alongside or instead of the existing Preview 2
7576"sync ABI" to call or implement * any* WIT function type, not just functions
7677with specific signatures. This allows * all* function types to be called or
@@ -83,6 +84,7 @@ pairings have well-defined, composable behavior for both inter-component and
8384intra-component calls, so that functions and components are not forced to pick
8485a "[ color] ".
8586
87+ TODO
8688Although Component Model function * types* are colorless, it can still be
8789beneficial, especially in languages with ` async ` /` await ` -style concurrency, to
8890give the bindings generator a * hint* as to whether or not a particular function
@@ -159,6 +161,7 @@ any more until I let some of them complete". Thus, the Component Model provides
159161a built-in way for a component instance to apply and release backpressure that
160162callers must always be prepared to handle.
161163
164+ TODO: ("through an ` async ` function type so that blocking doesn't trap")
162165With this backpressure mechanism in place, there is a natural way for sync and
163166async code to interoperate:
1641671 . If an async component calls a sync component and the sync component blocks,
@@ -558,6 +561,9 @@ attempting to `thread.resume-later` a thread waiting on `waitable-set.wait` or
558561a synchronous import call will trap. Thus, language runtimes and compilers have
559562to be careful when using a mix of explicit and implicit suspension/resumption.
560563
564+ TODO: suspend before returning value from a non-` async ` function traps,
565+ specifically: ` thread.suspend ` , ` waitable-set.wait ` . Also others
566+
561567Lastly, when an async function is implemented using the ` callback ` suboption
562568(mentioned in the [ summary] ( #summary ) ), instead of calling ` wait ` , ` poll ` or
563569` yield ` , as an optimization, the ` callback ` function can * return* to wait in
@@ -589,6 +595,12 @@ event, allowing a higher degree of concurrency than synchronous exports.
589595Stackfull async exports ignore the lock entirely and thus achieve the highest
590596degree of (cooperative) concurrency.
591597
598+ TODO: non-` async ` functions don't pile up like ` async ` functions and aren't
599+ allowed to block, and thus can and must skip backpressure. If a single
600+ component exports by ` async ` and non-` async ` functions, sync functions ignore
601+ implicit backpressure and thus barge in like signal handlers. Codegen must
602+ be aware.
603+
592604Once a task is allowed to start according to these backpressure rules, its
593605arguments are lowered into the callee's linear memory and the task is in
594606the "started" state.
@@ -619,6 +631,8 @@ transitively created by that thread) must call `task.return`.
619631Once ` task.return ` is called, the task is in the "returned" state. Calling
620632` task.return ` when not in the "started" state traps.
621633
634+ TODO: once in the "returned" state, even non-` async ` functions can block
635+
622636### Borrows
623637
624638Component Model async support is careful to ensure that ` borrow ` ed handles work
@@ -686,13 +700,14 @@ the next cancellable wait. In the worst case, though, a component may never
686700wait cancellably and thus cancellation may be silently ignored.
687701
688702` subtask.cancel ` can be called synchronously or asynchronously. If called
689- synchronously, ` subtask.cancel ` waits until the subtask reaches a resolved
690- state and returns which state was reached. If called asynchronously, then if a
691- cancellable subtask thread is resumed * and* the subtask reaches a resolved
692- state before suspending itself for whatever reason ` subtask.cancel ` will return
693- which state was reached. Otherwise, ` subtask.cancel ` will return a "blocked"
694- sentinel value and the caller must [ wait] [ waiting ] via waitable set until the
695- subtask reaches a resolved state.
703+ synchronously, ` subtask.cancel ` blocks until the subtask reaches a resolved
704+ state and returns which state was reached. (TODO: traps if not allowed to
705+ block) If called asynchronously, then if a cancellable subtask thread is
706+ resumed * and* the subtask reaches a resolved state before suspending itself for
707+ whatever reason ` subtask.cancel ` will return which state was reached.
708+ Otherwise, ` subtask.cancel ` will return a "blocked" sentinel value and the
709+ caller must [ wait] [ waiting ] via waitable set until the subtask reaches a
710+ resolved state.
696711
697712The Component Model does not provide a mechanism to force prompt termination of
698713threads as this can lead to leaks and corrupt state in a still-live component
@@ -807,8 +822,10 @@ function an async-oriented core function signature that can be used instead of
807822or in addition to the existing (Preview-2-defined) synchronous core function
808823signature. This async-oriented core function signature is intended to be called
809824or implemented by generated bindings which then map the low-level core async
810- protocol to the languages' higher-level native concurrency features. Because
811- the WIT-level ` async ` attribute is purely a * hint* (as mentioned
825+ protocol to the languages' higher-level native concurrency features.
826+
827+ TODO
828+ Because the WIT-level ` async ` attribute is purely a * hint* (as mentioned
812829[ above] ( #summary ) ), * every* WIT function has an async core function signature;
813830` async ` just provides hints to the bindings generator for which to use by
814831default.
@@ -818,10 +835,10 @@ default.
818835Given these imported WIT functions (using the fixed-length-list feature 🔧):
819836``` wit
820837world w {
821- import foo: func(s: string) -> u32;
822- import bar: func(s: string) -> string;
823- import baz: func(t: list<u64; 5>) -> string;
824- import quux: func(t: list<u32; 17>) -> string;
838+ import foo: async func(s: string) -> u32;
839+ import bar: async func(s: string) -> string;
840+ import baz: async func(t: list<u64; 5>) -> string;
841+ import quux: async func(t: list<u32; 17>) -> string;
825842}
826843```
827844the default/synchronous lowered import function signatures are:
@@ -871,22 +888,22 @@ receiving an event indicating that the async subtask has started/returned.
871888
872889Other example asynchronous lowered signatures:
873890
874- | WIT function type | Async ABI |
875- | ----------------------------------------- | --------------------- |
876- | ` func() ` | ` (func (result i32)) ` |
877- | ` func() -> string ` | ` (func (param $out-ptr i32) (result i32)) ` |
878- | ` func(x: f32) -> f32 ` | ` (func (param $x f32) (param $out-ptr i32) (result i32)) ` |
879- | ` func(s: string, t: string) ` | ` (func (param $s-ptr i32) (param $s-len i32) (result $t-ptr i32) (param $t-len i32) (result i32)) ` |
891+ | WIT function type | Async ABI |
892+ | ---------------------------------- | --------------------- |
893+ | ` async func()` | ` (func (result i32)) ` |
894+ | ` async func() -> string` | ` (func (param $out-ptr i32) (result i32)) ` |
895+ | ` async func(x: f32) -> f32` | ` (func (param $x f32) (param $out-ptr i32) (result i32)) ` |
896+ | ` async func(s: string, t: string)` | ` (func (param $s-ptr i32) (param $s-len i32) (result $t-ptr i32) (param $t-len i32) (result i32)) ` |
880897
881898` future ` and ` stream ` can appear anywhere in the parameter or result types. For example:
882899``` wit
883- func(s1: stream<future<string>>, s2: list<stream<string>>) -> result<stream<string>, stream<error>>
900+ async func(s1: stream<future<string>>, s2: list<stream<string>>) -> result<stream<string>, stream<error>>
884901```
885902In * both* the sync and async ABIs, a ` future ` or ` stream ` in the WIT-level type
886903translates to a single ` i32 ` in the ABI. This ` i32 ` is an index into the
887904current component instance's handle table. For example, for the WIT function type:
888905``` wit
889- func(f: future<string>) -> future<u32>
906+ async func(f: future<string>) -> future<u32>
890907```
891908the synchronous ABI has signature:
892909``` wat
@@ -911,7 +928,7 @@ Explainer]. For a complete description of how async imports work, see
911928Given an exported WIT function:
912929``` wit
913930world w {
914- export foo: func(s: string) -> string;
931+ export foo: async func(s: string) -> string;
915932}
916933```
917934
@@ -1004,7 +1021,7 @@ Starting with the stackful ABI, the meat of this example component is replaced
10041021with ` ... ` to focus on the overall flow of function calls:
10051022``` wat
10061023(component
1007- (import "fetch" (func $fetch (param "url" string) (result (list u8))))
1024+ (import "fetch" (func $fetch async (param "url" string) (result (list u8))))
10081025 (core module $Libc
10091026 (memory (export "mem") 1)
10101027 (func (export "realloc") (param i32 i32 i32 i32) (result i32) ...)
@@ -1064,7 +1081,7 @@ with `...` to focus on the overall flow of function calls:
10641081 ))))
10651082 (canon lift (core func $main "summarize")
10661083 async (memory $mem) (realloc $realloc)
1067- (func $summarize (param "urls" (list string)) (result string)))
1084+ (func $summarize async (param "urls" (list string)) (result string)))
10681085 (export "summarize" (func $summarize))
10691086)
10701087```
@@ -1087,6 +1104,10 @@ call to `waitable-set.wait` blocks, the runtime will suspend its callstack
10871104entry point and store it in context-local storage (via ` context.set ` ) instead
10881105of simply using a ` global ` , as in a synchronous function.
10891106
1107+ Note that removing ` async ` from the type of ` summarize ` specified in the `canon
1108+ lift` definition would cause the above component to trap when it attempted to
1109+ call ` waitable-set.wait ` .
1110+
10901111### Stackless ABI example
10911112
10921113The stackful example can be re-written to use the ` callback ` immediate (thereby
@@ -1102,7 +1123,7 @@ core wasm code between events, not externally-visible behavior.
11021123
11031124``` wat
11041125(component
1105- (import "fetch" (func $fetch (param "url" string) (result (list u8))))
1126+ (import "fetch" (func $fetch async (param "url" string) (result (list u8))))
11061127 (core module $Libc
11071128 (memory (export "mem") 1)
11081129 (func (export "realloc") (param i32 i32 i32 i32) (result i32) ...)
@@ -1169,7 +1190,7 @@ core wasm code between events, not externally-visible behavior.
11691190 ))))
11701191 (canon lift (core func $main "summarize")
11711192 async (callback (core func $main "cb")) (memory $mem) (realloc $realloc)
1172- (func $summarize (param "urls" (list string)) (result string)))
1193+ (func $summarize async (param "urls" (list string)) (result string)))
11731194 (export "summarize" (func $summarize))
11741195)
11751196```
0 commit comments