@@ -22,6 +22,9 @@ summary of the motivation and animated sketch of the design in action.
2222 * [ Waiting] ( #waiting )
2323 * [ Backpressure] ( #backpressure )
2424 * [ Returning] ( #returning )
25+ * [ Async ABI] ( #async-abi )
26+ * [ Async Import ABI] ( #async-import-abi )
27+ * [ Async Export ABI] ( #async-export-abi )
2528* [ Examples] ( #examples )
2629* [ Interaction with the start function] ( #interaction-with-the-start-function )
2730* [ Interaction with multi-threading] ( #interaction-with-multi-threading )
@@ -483,6 +486,169 @@ A task may not call `task.return` unless it is in the "started" state. Once
483486finish once it is in the "returned" state. See the [ ` canon_task_return ` ]
484487function in the Canonical ABI explainer for more details.
485488
489+ ## Async ABI
490+
491+ At an ABI level, native async in the Component Model defines for every WIT
492+ function an async-oriented core function signature that can be used instead of
493+ or in addition to the existing (Preview-2-defined) synchronous core function
494+ signature. This async-oriented core function signature is intended to be called
495+ or implemented by generated bindings which then map the low-level core async
496+ protocol to the languages' higher-level native concurrency features. Because
497+ the WIT-level ` async ` attribute is purely a * hint* (as mentioned
498+ [ above] ( #sync-and-async-functions ) ), * every* WIT function has an async core
499+ function signature; ` async ` just provides hints to the bindings generator for
500+ which to use by default.
501+
502+ ### Async Import ABI
503+
504+ Given an imported WIT function:
505+ ``` wit
506+ world w {
507+ import foo: func(s: string) -> string;
508+ }
509+ ```
510+ the default sync import function signature is:
511+ ``` wat
512+ ;; sync
513+ (func (param $s-ptr i32) (param $s-len i32) (param $out i32))
514+ ```
515+ where ` $out ` must be a 4-byte-aligned pointer into linear memory into which the
516+ 8-byte (pointer, length) of the returned string will be stored.
517+
518+ The new async import function signature is:
519+ ``` wat
520+ ;; async
521+ (func (param $in i32) (param $out i32) (result i32))
522+ ```
523+ where ` $in ` must be a 4-byte-aligned pointer into linear memory from which the
524+ 8-byte (pointer, length) of the string argument will be loaded and ` $out ` works
525+ the same as in the synchronous case. What's different, however, is * when* ` $in `
526+ and ` $out ` are read or written. In a synchronous call, they are always read or
527+ written before the call returns. In an asynchronous call, there is a set of
528+ possibilities indicated by the ` (result i32) ` value:
529+ * If the returned ` i32 ` is ` 0 ` , then the call completed synchronously without
530+ blocking and so ` $in ` has been read and ` $out ` has been written.
531+ * Otherwise, the high 28 bits of the ` i32 ` are the index of a new ` Subtask `
532+ in the current component instance's ` waitables ` table. The low 4 bits
533+ indicate how far the callee made it before blocking:
534+ * If ` 1 ` , the callee didn't even start (due to backpressure), and thus
535+ neither ` $in ` nor ` $out ` have been accessed yet.
536+ * If ` 2 ` , the callee started by reading ` $in ` , but blocked before writing
537+ ` $out ` .
538+
539+ The async signature ` (func (param i32 i32) (result i32)) ` is the same for
540+ almost all WIT function types since the ABI stores everything in linear memory.
541+ However, there are three special cases:
542+ * If the WIT parameter list is empty, ` $in ` is removed.
543+ * If the WIT parameter list flattens to exactly 1 core value type (` i32 ` or
544+ otherwise), ` $in ` uses that core value type and the argument is passed
545+ by value.
546+ * If the WIT result is empty, ` $out ` is removed.
547+
548+ For example:
549+ | WIT function type | Async ABI |
550+ | ----------------------------------------- | --------------------- |
551+ | ` func() ` | ` (func (result i32)) ` |
552+ | ` func() -> string ` | ` (func (param $out i32) (result i32)) ` |
553+ | ` func(s: string) ` | ` (func (param $in i32) (result i32)) ` |
554+ | ` func(x: f32) -> f32 ` | ` (func (param $in f32) (param $out i32) (result i32)) ` |
555+ | ` func(x: list<list<u8>>) -> list<string> ` | ` (func (param $in i32) (param $out i32) (result i32)) ` |
556+
557+ ` future ` and ` stream ` can appear anywhere in the parameter or result types. For example:
558+ ``` wit
559+ func(s1: stream<future<string>>, s2: list<stream<string>>) -> result<stream<string>, stream<error>>
560+ ```
561+ In * both* the sync and async ABIs, a ` future ` or ` stream ` in the WIT-level type
562+ translates to a single ` i32 ` in the ABI. This ` i32 ` is an index into the
563+ component instance's ` waitables ` table. For example, for the WIT function type:
564+ ``` wit
565+ func(f: future<string>) -> future<u32>
566+ ```
567+ the synchronous ABI has signature:
568+ ``` wat
569+ (func (param $f i32) (result i32))
570+ ```
571+ and the asynchronous ABI has the signature:
572+ ``` wat
573+ (func (param $in i32) (param $out i32) (result i32))
574+ ```
575+ where, according to the above rules, ` $in ` is the index of a future in the
576+ ` waitables ` table (not a pointer to one) while ` $out ` is a pointer to a linear
577+ memory location that will receive an ` i32 ` index.
578+
579+ For the runtime semantics of this ` i32 ` index, see ` lift_stream ` ,
580+ ` lift_future ` , ` lower_stream ` and ` lower_future ` in the [ Canonical ABI
581+ Explainer] . For a complete description of how async imports work, see
582+ [ ` canon_lower ` ] in the Canonical ABI Explainer.
583+
584+
585+ #### Async Export ABI
586+
587+ Given an exported WIT function:
588+ ``` wit
589+ world w {
590+ export foo: func(s: string) -> string;
591+ }
592+ ```
593+ the default sync export function signature is:
594+ ``` wat
595+ ;; sync
596+ (func (param $s-ptr i32) (param $s-len i32) (result $retp i32))
597+ ```
598+ where (working around the continued lack of multi-return support throughout
599+ the core wasm toolchain) ` $retp ` must be a 4-byte-aligned pointer into linear
600+ memory from which the 8-byte (pointer, length) of the string result can be
601+ loaded.
602+
603+ The async export ABI provides two flavors: stackful and stackless.
604+
605+ The async stackful export function signature is:
606+ ``` wat
607+ ;; async, no callback
608+ (func (param $s-ptr i32) (param $s-len i32))
609+ ```
610+ The parameters work just like synchronous parameters. There is no core function
611+ result because a callee [ returns] ( #returning ) their value by * calling* the
612+ * imported* ` task.return ` function which has signature:
613+ ``` wat
614+ ;; task.return
615+ (func (param $ret-ptr i32) (result $ret-len i32))
616+ ```
617+ The parameters of ` task.return ` work the same as if the WIT return type was the
618+ WIT parameter type of a synchronous function. For example, if more than 16
619+ core parameters would be needed, a single ` i32 ` pointer into linear memory is
620+ used.
621+
622+ The async stackless export function signature is:
623+ ``` wat
624+ ;; async, callback
625+ (func (param $s-ptr i32) (param $s-len i32) (result i32))
626+ ```
627+ The parameters also work just like synchronous parameters. The callee returns
628+ their value by calling ` task.return ` just like the stackful case. The `(result
629+ i32)` lets the core function return what it wants the runtime to do next:
630+ * If the low 4 bits are ` 0 ` , the callee completed (and called ` task.return ` )
631+ without blocking.
632+ * If the low 4 bits are ` 1 ` , the callee wants to yield, allowing other code
633+ to run, but resuming thereafter without waiting on anything else.
634+ * If the low 4 bits are ` 2 ` , the callee wants to wait for an event to occur in
635+ the waitable set whose index is stored in the high 28 bits.
636+ * If the low 4 bits are ` 3 ` , the callee wants to poll for any events that have
637+ occurred in the waitable set whose index is stored in the high 28 bits.
638+
639+ When an async stackless function is exported, a companion "callback" function
640+ must also be exported with signature:
641+ ``` wat
642+ (func (param i32 i32 i32) (result i32))
643+ ```
644+ The ` (result i32) ` has the same interpretation as the stackless export function
645+ and the runtime will repeatedly call the callback until a value of ` 0 ` is
646+ returned. The ` i32 ` parameters describe what happened that caused the callback
647+ to be called again.
648+
649+ For a complete description of how async exports work, see [ ` canon_lift ` ] in the
650+ Canonical ABI Explainer.
651+
486652
487653## Examples
488654
@@ -784,6 +950,7 @@ comes after:
784950[ ESM-integration ] : Explainer.md#ESM-integration
785951
786952[ Canonical ABI Explainer ] : CanonicalABI.md
953+ [ ABI Options ] : CanonicalABI.md#canonical-abi-options
787954[ `canon_lift` ] : CanonicalABI.md#canon-lift
788955[ `unpack_callback_result` ] : CanonicalABI.md#canon-lift
789956[ `canon_lower` ] : CanonicalABI.md#canon-lower
0 commit comments