@@ -186,22 +186,23 @@ interface filesystem {
186
186
}
187
187
}
188
188
```
189
- a bindings generator in a language with ` async ` would only emit ` async `
190
- functions for ` read ` and ` fetch ` . Since in many languages ` new ` expressions
191
- cannot be async, there is no ` async constructor ` . Use cases requiring
192
- asynchronous construction can instead use ` static async ` functions, similar to
193
- ` from-stream ` in this example.
189
+ A bindings generator processing the above WIT for a language with ` async ` would
190
+ only emit ` async ` functions for ` read ` and ` from-stream ` .
194
191
192
+ Since in many languages ` new ` expressions cannot be async, there is no
193
+ ` async constructor ` . Use cases requiring asynchronous construction can instead
194
+ use ` static async ` functions, similar to ` from-stream ` in this example.
195
195
196
196
### Task
197
197
198
198
Every time a lifted function is called (e.g., when a component's export is
199
199
called by the outside world), a new ** task** is created that logically contains
200
200
all the transitive control-flow state of the export call and will be destroyed
201
- when the export call finishes. When all of a component's exports are lifted
202
- synchronously, there will be at most one task alive at any one time. However,
203
- when a component exports asynchronously-lifted functions, there can be multiple
204
- tasks alive at once.
201
+ when the export call finishes.
202
+
203
+ When all of a component's exports are lifted synchronously, there will be at most one
204
+ task alive at any one time. However, when a component exports asynchronously-lifted
205
+ functions, there can be multiple tasks alive at once.
205
206
206
207
In the Canonical ABI explainer, a "task" is represented with the Python
207
208
[ ` Task ` ] class. A new ` Task ` object is created (by [ ` canon_lift ` ] ) each time
@@ -268,18 +269,27 @@ in the Canonical ABI explainer.
268
269
### Structured concurrency
269
270
270
271
Calling * into* a component creates a ` Task ` to track ABI state related to the
271
- * callee* (like "number of outstanding borrows"). Calling * out* of a component
272
- creates a ` Subtask ` to track ABI state related to the * caller* (like "which
273
- handles have been lent"). When one component calls another, there is thus a
274
- ` Subtask ` +` Task ` pair that collectively maintains the overall state of the call
275
- and enforces that both components uphold their end of the ABI contract. But
276
- when the host calls into a component, there is only a ` Task ` and,
277
- symmetrically, when a component calls into the host, there is only a ` Subtask ` .
278
-
279
- Based on this, the call stack at any point in time when a component calls a
280
- host-defined import will have a callstack of the general form:
272
+ * callee* (like "number of outstanding borrows").
273
+
274
+ Calling * out* of a component creates a ` Subtask ` to track ABI state related to
275
+ the * caller* (like "which handles have been lent").
276
+
277
+ When one component calls another, there is thus a ` Subtask ` +` Task ` pair that
278
+ collectively maintains the overall state of the call and enforces that both
279
+ components uphold their end of the ABI contract. But when the host calls into
280
+ a component, there is only a ` Task ` and, symmetrically, when a component calls
281
+ into the host, there is only a ` Subtask ` .
282
+
283
+ Based on this, the call stack when a component calls a host-defined import will
284
+ have the general form:
281
285
```
282
- [Host caller] <- [Task] <- [Subtask+Task]* <- [Subtask] <- [Host callee]
286
+ [Host]
287
+ ↓ host calls component export
288
+ [Component Task]
289
+ ↓ component calls import implemented by another component's export 0..N times
290
+ [Component Subtask <> Component Task]*
291
+ ↓ component calls import implemented by the host
292
+ [Component Subtask <> Host task]
283
293
```
284
294
Here, the ` <- ` arrow represents the ` supertask ` relationship that is immutably
285
295
established when first making the call. A paired ` Subtask ` and ` Task ` have the
@@ -719,7 +729,8 @@ world w {
719
729
export foo: func(s: string) -> string;
720
730
}
721
731
```
722
- the default sync export function signature is:
732
+
733
+ The default sync export function signature for export ` foo ` is:
723
734
``` wat
724
735
;; sync
725
736
(func (param $s-ptr i32) (param $s-len i32) (result $retp i32))
@@ -735,33 +746,40 @@ The async export ABI provides two flavors: stackful and stackless.
735
746
736
747
The stackful ABI is currently gated by the 🚟 feature.
737
748
738
- The async stackful export function signature is:
749
+ The async stackful export function signature for export ` foo ` (defined above
750
+ in world ` w ` ) is:
739
751
``` wat
740
752
;; async, no callback
741
753
(func (param $s-ptr i32) (param $s-len i32))
742
754
```
743
- The parameters work just like synchronous parameters. There is no core function
744
- result because a callee [ returns] ( #returning ) their value by * calling* the
745
- * imported* ` task.return ` function which has signature:
755
+
756
+ The parameters work just like synchronous parameters.
757
+
758
+ There is no core function result because a callee [ returns] ( #returning ) their
759
+ value by * calling* the * imported* ` task.return ` function which has signature:
746
760
``` wat
747
761
;; task.return
748
762
(func (param $ret-ptr i32) (result $ret-len i32))
749
763
```
764
+
750
765
The parameters of ` task.return ` work the same as if the WIT return type was the
751
766
WIT parameter type of a synchronous function. For example, if more than 16
752
767
core parameters would be needed, a single ` i32 ` pointer into linear memory is
753
768
used.
754
769
755
770
##### Stackless Async Exports
756
771
757
- The async stackless export function signature is:
772
+ The async stackless export function signature for export ` foo ` (defined above
773
+ in world ` w ` ) is:
758
774
``` wat
759
775
;; async, callback
760
776
(func (param $s-ptr i32) (param $s-len i32) (result i32))
761
777
```
778
+
762
779
The parameters also work just like synchronous parameters. The callee returns
763
- their value by calling ` task.return ` just like the stackful case. The `(result
764
- i32)` lets the core function return what it wants the runtime to do next:
780
+ their value by calling ` task.return ` just like the stackful case.
781
+
782
+ The ` (result i32) ` lets the core function return what it wants the runtime to do next:
765
783
* If the low 4 bits are ` 0 ` , the callee completed (and called ` task.return ` )
766
784
without blocking.
767
785
* If the low 4 bits are ` 1 ` , the callee wants to yield, allowing other code
@@ -776,6 +794,7 @@ must also be exported with signature:
776
794
``` wat
777
795
(func (param i32 i32 i32) (result i32))
778
796
```
797
+
779
798
The ` (result i32) ` has the same interpretation as the stackless export function
780
799
and the runtime will repeatedly call the callback until a value of ` 0 ` is
781
800
returned. The ` i32 ` parameters describe what happened that caused the callback
0 commit comments