@@ -620,58 +620,68 @@ which to use by default.
620
620
621
621
### Async Import ABI
622
622
623
- Given an imported WIT function :
623
+ Given these imported WIT functions (using the fixed-length-list feature 🔧) :
624
624
``` wit
625
625
world w {
626
- import foo: func(s: string) -> string;
626
+ import foo: func(s: string) -> u32;
627
+ import bar: func(s: string) -> string;
628
+ import baz: func(t: tuple<u64; 5>) -> string;
629
+ import quux: func(t: tuple<u32; 17>) -> string;
627
630
}
628
631
```
629
- the default sync import function signature is :
632
+ the default/synchronous lowered import function signatures are :
630
633
``` wat
631
634
;; sync
632
- (func (param $s-ptr i32) (param $s-len i32) (param $out i32))
635
+ (func $foo (param $s-ptr i32) (param $s-len i32) (result i32))
636
+ (func $bar (param $s-ptr i32) (param $s-len i32) (param $out-ptr i32))
637
+ (func $baz (param i64 i64 i64 i64 i64) (param $out-ptr i32))
638
+ (func $quux (param $in-ptr i32) (param $out-ptr i32))
633
639
```
634
- where ` $out ` must be a 4-byte-aligned pointer into linear memory into which the
635
- 8-byte (pointer, length) of the returned string will be stored.
636
-
637
- The new async import function signature is:
640
+ Here: ` foo ` , ` bar ` and ` baz ` pass their parameters as "flattened" core value
641
+ types while ` quux ` passes its parameters via the ` $in-ptr ` linear memory
642
+ pointer (due to the Canonical ABI limitation of 16 maximum flattened
643
+ parameters). Similarly, ` foo ` returns its result as a single core value while
644
+ ` bar ` , ` baz ` and ` quux ` return their results via the ` $out-ptr ` linear memory
645
+ pointer (due to the current Canonical ABI limitation of 1 maximum flattened
646
+ result).
647
+
648
+ The corresponding asynchronous lowered import function signatures are:
638
649
``` wat
639
650
;; async
640
- (func (param $in i32) (param $out i32) (result i32))
651
+ (func $foo (param $s-ptr i32) (param $s-len i32) (param $out-ptr i32) (result i32))
652
+ (func $bar (param $s-ptr i32) (param $s-len i32) (param $out-ptr i32) (result i32))
653
+ (func $baz (param $in-ptr i32) (param $out-ptr i32) (result i32))
654
+ (func $quux (param $in-ptr i32) (param $out-ptr i32) (result i32))
641
655
```
642
- where ` $in ` must be a 4-byte-aligned pointer into linear memory from which the
643
- 8-byte (pointer, length) of the string argument will be loaded and ` $out ` works
644
- the same as in the synchronous case. What's different, however, is * when* ` $in `
645
- and ` $out ` are read or written. In a synchronous call, they are always read or
646
- written before the call returns. In an asynchronous call, there is a set of
647
- possibilities indicated by the ` (result i32) ` value:
648
- * If the returned ` i32 ` is ` 2 ` , then the call returned eagerly without
649
- blocking and so ` $in ` has been read and ` $out ` has been written.
650
- * Otherwise, the high 28 bits of the ` i32 ` are the index of a new ` Subtask `
651
- in the current component instance's table. The low 4 bits indicate how far
652
- the callee made it before blocking:
653
- * If ` 1 ` , the callee didn't even start (due to backpressure), and thus
654
- neither ` $in ` nor ` $out ` have been accessed yet.
655
- * If ` 2 ` , the callee started by reading ` $in ` , but blocked before writing
656
- ` $out ` .
657
-
658
- The async signature ` (func (param i32 i32) (result i32)) ` is the same for
659
- almost all WIT function types since the ABI stores everything in linear memory.
660
- However, there are three special cases:
661
- * If the WIT parameter list is empty, ` $in ` is removed.
662
- * If the WIT parameter list flattens to exactly 1 core value type (` i32 ` or
663
- otherwise), ` $in ` uses that core value type and the argument is passed
664
- by value.
665
- * If the WIT result is empty, ` $out ` is removed.
666
-
667
- For example:
656
+ Comparing signatures, the differences are:
657
+ * Async-lowered functions have a maximum of 4 flat parameters (not 16).
658
+ * Async-lowered functions always return their value via linear memory pointer.
659
+ * Async-lowered functions always have a single ` i32 ` "status" code.
660
+
661
+ Additionally, * when* the parameter and result pointers are read/written depends
662
+ on the status code:
663
+ * If the low 4 bits of the status are ` 0 ` , the call didn't even start and so
664
+ ` $in-ptr ` hasn't been read and ` $out-ptr ` hasn't been written and the high
665
+ 28 bits are the index of a new async subtask to wait on.
666
+ * If the low 4 bits of the status are ` 1 ` , the call started, ` $in-ptr ` was
667
+ read, but ` $out-ptr ` hasn't been written and the high 28 bits are the index
668
+ of a new async subtask to wait on.
669
+ * If the low 4 bits of the status are ` 2 ` , the call returned and so ` $in-ptr `
670
+ and ` $out-ptr ` have been read/written and the high 28 bits are ` 0 ` because
671
+ there is no async subtask to wait on.
672
+
673
+ When a parameter/result pointer hasn't yet been read/written, the async caller
674
+ must take care to keep the region of memory allocated to the call until
675
+ receiving an event indicating that the async subtask has started/returned.
676
+
677
+ Other example asynchronous lowered signatures:
678
+
668
679
| WIT function type | Async ABI |
669
680
| ----------------------------------------- | --------------------- |
670
681
| ` func() ` | ` (func (result i32)) ` |
671
- | ` func() -> string ` | ` (func (param $out i32) (result i32)) ` |
672
- | ` func(s: string) ` | ` (func (param $in i32) (result i32)) ` |
673
- | ` func(x: f32) -> f32 ` | ` (func (param $in f32) (param $out i32) (result i32)) ` |
674
- | ` func(x: list<list<u8>>) -> list<string> ` | ` (func (param $in i32) (param $out i32) (result i32)) ` |
682
+ | ` func() -> string ` | ` (func (param $out-ptr i32) (result i32)) ` |
683
+ | ` func(x: f32) -> f32 ` | ` (func (param $x f32) (param $out-ptr i32) (result i32)) ` |
684
+ | ` func(s: string, t: string) ` | ` (func (param $s-ptr i32) (param $s-len i32) (result $t-ptr i32) (param $t-len i32) (result i32)) ` |
675
685
676
686
` future ` and ` stream ` can appear anywhere in the parameter or result types. For example:
677
687
``` wit
@@ -689,11 +699,11 @@ the synchronous ABI has signature:
689
699
```
690
700
and the asynchronous ABI has the signature:
691
701
``` wat
692
- (func (param $in i32) (param $out i32) (result i32))
702
+ (func (param $f i32) (param $out-ptr i32) (result i32))
693
703
```
694
- where, according to the above rules, ` $in ` is the index of a future in the
695
- current component instance's table (not a pointer to one) while ` $out ` is a
696
- pointer to a linear memory location that will receive an ` i32 ` index.
704
+ where ` $f ` is the index of a future (not a pointer to one) while while
705
+ ` $out-ptr ` is a pointer to a linear memory location that will receive an ` i32 `
706
+ index.
697
707
698
708
For the runtime semantics of this ` i32 ` index, see ` lift_stream ` ,
699
709
` lift_future ` , ` lower_stream ` and ` lower_future ` in the [ Canonical ABI
@@ -786,7 +796,7 @@ replaced with `...` to focus on the overall flow of function calls.
786
796
(core module $Main
787
797
(import "libc" "mem" (memory 1))
788
798
(import "libc" "realloc" (func (param i32 i32 i32 i32) (result i32)))
789
- (import "" "fetch" (func $fetch (param i32 i32) (result i32)))
799
+ (import "" "fetch" (func $fetch (param i32 i32 i32 ) (result i32)))
790
800
(import "" "waitable-set.new" (func $new_waitable_set (result i32)))
791
801
(import "" "waitable-set.wait" (func $wait (param i32 i32) (result i32)))
792
802
(import "" "waitable.join" (func $join (param i32 i32)))
@@ -800,7 +810,7 @@ replaced with `...` to focus on the overall flow of function calls.
800
810
...
801
811
loop
802
812
...
803
- call $fetch ;; pass a pointer-to- string and pointer-to-list-of-bytes outparam
813
+ call $fetch ;; pass a string pointer, string length and pointer-to-list-of-bytes outparam
804
814
... ;; ... and receive the index of a new async subtask
805
815
global.get $wsi
806
816
call $join ;; ... and add it to the waitable set
@@ -878,7 +888,7 @@ not externally-visible behavior.
878
888
(core module $Main
879
889
(import "libc" "mem" (memory 1))
880
890
(import "libc" "realloc" (func (param i32 i32 i32 i32) (result i32)))
881
- (import "" "fetch" (func $fetch (param i32 i32) (result i32)))
891
+ (import "" "fetch" (func $fetch (param i32 i32 i32 ) (result i32)))
882
892
(import "" "waitable-set.new" (func $new_waitable_set (result i32)))
883
893
(import "" "waitable.join" (func $join (param i32 i32)))
884
894
(import "" "task.return" (func $task_return (param i32 i32)))
@@ -891,7 +901,7 @@ not externally-visible behavior.
891
901
...
892
902
loop
893
903
...
894
- call $fetch ;; pass a pointer-to- string and pointer-to-list-of-bytes outparam
904
+ call $fetch ;; pass a string pointer, string length and pointer-to-list-of-bytes outparam
895
905
... ;; ... and receive the index of a new async subtask
896
906
global.get $wsi
897
907
call $join ;; ... and add it to the waitable set
0 commit comments