@@ -22,6 +22,9 @@ summary of the motivation and animated sketch of the design in action.
22
22
* [ Waiting] ( #waiting )
23
23
* [ Backpressure] ( #backpressure )
24
24
* [ Returning] ( #returning )
25
+ * [ Async ABI] ( #async-abi )
26
+ * [ Async Import ABI] ( #async-import-abi )
27
+ * [ Async Export ABI] ( #async-export-abi )
25
28
* [ Examples] ( #examples )
26
29
* [ Interaction with the start function] ( #interaction-with-the-start-function )
27
30
* [ 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
483
486
finish once it is in the "returned" state. See the [ ` canon_task_return ` ]
484
487
function in the Canonical ABI explainer for more details.
485
488
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
+
486
652
487
653
## Examples
488
654
@@ -784,6 +950,7 @@ comes after:
784
950
[ ESM-integration ] : Explainer.md#ESM-integration
785
951
786
952
[ Canonical ABI Explainer ] : CanonicalABI.md
953
+ [ ABI Options ] : CanonicalABI.md#canonical-abi-options
787
954
[ `canon_lift` ] : CanonicalABI.md#canon-lift
788
955
[ `unpack_callback_result` ] : CanonicalABI.md#canon-lift
789
956
[ `canon_lower` ] : CanonicalABI.md#canon-lower
0 commit comments