Skip to content

Commit a96ef45

Browse files
committed
Add 'async' hint to functions
1 parent 5c54dcd commit a96ef45

File tree

3 files changed

+73
-16
lines changed

3 files changed

+73
-16
lines changed

design/mvp/Async.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,40 @@ component-level function that has been [lifted] from Core WebAssembly with the
153153
function that does not have the `async` option set (which is the default and
154154
only option prior to Preview 3). Thus, the sync/async distinction appears
155155
only independently in how a component-level function is *implemented* or
156-
*called*.
156+
*called*. This lack of distinction helps to avoid the classic ["What Color Is
157+
Your Function?"][Color] problem.
158+
159+
Functions *may* however be annotated (in both WIT and component binaries) with
160+
`async` as a *hint*. This hint is intended to inform the default language
161+
bindings generation, indicating whether to use a source-level `async` function
162+
or not in languages that have such a distinction (e.g., JS, Python, C# and
163+
Rust). In the absence of such a hint, a bindings generator would be forced to
164+
make a uniform decision for what to do by default for all functions or require
165+
manual programmer directives. However, because `async` is just a hint, there is
166+
no prohibition against non-`async`-exported functions calling imported `async`
167+
functions. This does mean that non-`async` functions may end up blocking
168+
their caller, but (1) any loss in performance is the callee's "fault", (2) the
169+
caller can still lower `async` if they want to (overriding the default hint),
170+
(3) any *transitive* caller an lower `async` to avoid blocking.
171+
172+
For example, given this interface:
173+
```wit
174+
interface filesystem {
175+
resource file {
176+
constructor();
177+
is-closed: func() -> bool;
178+
read: async func(num-bytes: u32) -> result<list<u8>>;
179+
from-bytes: static func(bytes: list<u8>) -> file;
180+
from-stream: static async func(bytes: stream<u8>) -> file;
181+
}
182+
}
183+
```
184+
a bindings generator in a language with `async` would only emit `async`
185+
functions for `read` and `fetch`. Since in many languages `new` expressions
186+
cannot be async, there is no `async constructor`. Use cases requiring
187+
asynchronous construction can instead use `static async` functions, similar to
188+
`from-stream` in this example.
189+
157190

158191
### Task
159192

@@ -708,11 +741,9 @@ time as applications are built with more components.
708741
## TODO
709742

710743
Native async support is being proposed incrementally. The following features
711-
will be added in future chunks roughly in the order list to complete the full
744+
will be added in future chunks roughly in the order listed to complete the full
712745
"async" story, with a TBD cutoff between what's in [WASI Preview 3] and what
713746
comes after:
714-
* `nonblocking` function type attribute: allow a function to declare in its
715-
type that it will not transitively do anything blocking
716747
* `subtask.cancel`: allow a supertask to signal to a subtask that its result is
717748
no longer wanted and to please wrap it up promptly
718749
* zero-copy forwarding/splicing

design/mvp/Explainer.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,9 +2209,12 @@ importname ::= <exportname>
22092209
| <urlname>
22102210
| <hashname>
22112211
plainname ::= <label>
2212+
| '[async]' <label> 🔀
22122213
| '[constructor]' <label>
22132214
| '[method]' <label> '.' <label>
2215+
| '[async method]' <label> '.' <label> 🔀
22142216
| '[static]' <label> '.' <label>
2217+
| '[async static]' <label> '.' <label> 🔀
22152218
label ::= <fragment>
22162219
| <label> '-' <fragment>
22172220
fragment ::= <word>
@@ -2325,16 +2328,24 @@ The `plainname` production captures several language-neutral syntactic hints
23252328
that allow bindings generators to produce more idiomatic bindings in their
23262329
target language. At the top-level, a `plainname` allows functions to be
23272330
annotated as being a constructor, method or static function of a preceding
2328-
resource. In each of these cases, the first `label` is the name of the resource
2329-
and the second `label` is the logical field name of the function. This
2330-
additional nesting information allows bindings generators to insert the
2331-
function into the nested scope of a class, abstract data type, object,
2332-
namespace, package, module or whatever resources get bound to. For example, a
2333-
function named `[method]C.foo` could be bound in C++ to a member function `foo`
2334-
in a class `C`. The JS API [below](#JS-API) describes how the native JavaScript
2335-
bindings could look. Validation described in [Binary.md](Binary.md) inspects
2336-
the contents of `plainname` and ensures that the function has a compatible
2337-
signature.
2331+
resource and/or being asynchronous.
2332+
2333+
When a function is annotated with `constructor`, `method` or `static`, the
2334+
first `label` is the name of the resource and the second `label` is the logical
2335+
field name of the function. This additional nesting information allows bindings
2336+
generators to insert the function into the nested scope of a class, abstract
2337+
data type, object, namespace, package, module or whatever resources get bound
2338+
to. For example, a function named `[method]C.foo` could be bound in C++ to a
2339+
member function `foo` in a class `C`. The JS API [below](#JS-API) describes how
2340+
the native JavaScript bindings could look. Validation described in
2341+
[Binary.md](Binary.md) inspects the contents of `plainname` and ensures that
2342+
the function has a compatible signature.
2343+
2344+
When a function is annotated with `async`, bindings generators are expected to
2345+
emit whatever asynchronous language construct is appropriate (such as an
2346+
`async` function in JS, Python or Rust). Note the absence of
2347+
`[async constructor]`. See the [async
2348+
explainer](Async.md#sync-and-async-functions) for more details.
23382349

23392350
The `label` production used inside `plainname` as well as the labels of
23402351
`record` and `variant` types are required to have [kebab case]. The reason for

design/mvp/WIT.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,7 @@ keywords is still in flux at this time but the current set is:
862862

863863
```ebnf
864864
keyword ::= 'as'
865+
| 'async'
865866
| 'bool'
866867
| 'borrow'
867868
| 'char'
@@ -1299,7 +1300,7 @@ typedef-item ::= resource-item
12991300
13001301
func-item ::= id ':' func-type ';'
13011302
1302-
func-type ::= 'func' param-list result-list
1303+
func-type ::= 'async'? 'func' param-list result-list
13031304
13041305
param-list ::= '(' named-type-list ')'
13051306
@@ -1312,6 +1313,17 @@ named-type-list ::= ϵ
13121313
named-type ::= id ':' ty
13131314
```
13141315

1316+
The optional `async` hint in a WIT function type indicates that the callee
1317+
is expected to block and thus the caller should emit whatever asynchronous
1318+
language bindings are appropriate (e.g., in JS, Python, C# or Rust, an `async`
1319+
WIT function would emit an `async` JS/Python/C#/Rust function). Because `async`
1320+
is just a hint and not enforced by the runtime, it is technically possible for
1321+
a non-`async` callee to block. In that case, though, it is the *callee's* fault
1322+
for any resultant loss of concurrency, not the caller's. Thus, `async` is
1323+
primarily intended to document expectations in a way that can be taken
1324+
advantage of by bindings generators. (For more details, see the [async
1325+
explainer](Async.md#sync-and-async-functions).)
1326+
13151327

13161328
## Item: `use`
13171329

@@ -1528,10 +1540,13 @@ Specifically, the syntax for a `resource` definition is:
15281540
resource-item ::= 'resource' id ';'
15291541
| 'resource' id '{' resource-method* '}'
15301542
resource-method ::= func-item
1531-
| id ':' 'static' func-type ';'
1543+
| id ':' 'static' 'async'? func-type ';'
15321544
| 'constructor' param-list ';'
15331545
```
15341546

1547+
The optional `async` hint on `static` functions has the same meaning as
1548+
in a non-`static` `func-item`.
1549+
15351550
The syntax for handle types is presented [below](#handles).
15361551

15371552
## Types

0 commit comments

Comments
 (0)