Skip to content

Commit

Permalink
Add 'non-blocking' function attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
lukewagner committed Jan 21, 2025
1 parent f0dab1b commit d1db043
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 12 deletions.
32 changes: 29 additions & 3 deletions design/mvp/Async.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,34 @@ component-level function that has been [lifted] from Core WebAssembly with the
function that does not have the `async` option set (which is the default and
only option prior to Preview 3). Thus, the sync/async distinction appears
only independently in how a component-level function is *implemented* or
*called*.
*called*. This lack of distinction helps to avoid the classic ["What Color Is
Your Function?"][Color] problem.

Function types *may* be given an optional `non-blocking` function attribute to
indicate that the caller should assume that the callee doesn't block. However,
`non-blocking` is ignored at validation- and run-time and thus a `non-blocking`
function may in fact block (and indeed there are valid use cases for doing so,
such as in performance-insensitive virtualization scenarios). Therefore,
`non-blocking` is primarily intended as a *hint* to inform source-language
bindings to generate synchronous source-level function signatures (lifted and
and lowered without setting the `async` Canonical ABI option). Importantly,
since "blocking" is the default, interface authors are encouraged to apply
`non-blocking` judiciously.

Since in many languages `new` expressions cannot be async, `constructor`
functions also imply `non-blocking`. In the future, [getters and setters] would
also imply `non-blocking`. For example:
```wit
interface filesystem {
resource file {
constructor(); // non-blocking by default
is-closed: non-blocking func() -> bool; // explicitly non-blocking
read: func(num-bytes: u32) -> result<list<u8>>; // blocking by default
```
If a resource-type has a potentially-blocking constructor, it can simply use
`static new: func(...) -> my-resource` instead; `constructor` has no advantages
beyond more-idiomatic bindings generation in some languages.


### Task

Expand Down Expand Up @@ -591,8 +618,6 @@ Native async support is being proposed incrementally. The following features
will be added in future chunks roughly in the order list to complete the full
"async" story, with a TBD cutoff between what's in [WASI Preview 3] and what
comes after:
* `nonblocking` function type attribute: allow a function to declare in its
type that it will not transitively do anything blocking
* define what `async` means for `start` functions (top-level await + background
tasks), along with cross-task coordination built-ins
* `subtask.cancel`: allow a supertask to signal to a subtask that its result is
Expand Down Expand Up @@ -646,6 +671,7 @@ comes after:
[Use Cases]: ../high-level/UseCases.md
[Blast Zone]: FutureFeatures.md#blast-zones
[Reentrance]: Explainer.md#component-invariants
[Getters And Setters]: https://github.com/WebAssembly/component-model/issues/235

[stack-switching]: https://github.com/WebAssembly/stack-switching/
[JSPI]: https://github.com/WebAssembly/js-promise-integration/
Expand Down
17 changes: 13 additions & 4 deletions design/mvp/Binary.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ defvaltype ::= pvt:<primvaltype> => pvt
| 0x6a t?:<valtype>? u?:<valtype>? => (result t? (error u)?)
| 0x69 i:<typeidx> => (own i)
| 0x68 i:<typeidx> => (borrow i)
| 0x66 i?:<typeidx>? => (stream i?)
| 0x65 i?:<typeidx>? => (future i?)
| 0x66 i?:<typeidx>? => (stream i?) 🔀
| 0x65 i?:<typeidx>? => (future i?) 🔀
labelvaltype ::= l:<label'> t:<valtype> => l t
case ::= l:<label'> t?:<valtype>? 0x00 => (case l t?)
label' ::= len:<u32> l:<label> => l (if len = |l|)
Expand All @@ -214,7 +214,11 @@ valtype ::= i:<typeidx> => i
| pvt:<primvaltype> => pvt
resourcetype ::= 0x3f 0x7f f?:<funcidx>? => (resource (rep i32) (dtor f)?)
| 0x3e 0x7f f:<funcidx> cb?:<funcidx>? => (resource (rep i32) (dtor async f (callback cb)?))
functype ::= 0x40 ps:<paramlist> rs:<resultlist> => (func ps rs)
functype ::= 0x40 sig:<funcsig> => (func sig)
| 0x3d attrs:<funcattrs> sig:<funcsig> => (func attrs sig) 🔀
funcattrs ::= 0x00 =>
| 0x01 => non-blocking
funcsig ::= ps:<paramlist> rs:<resultlist> => ps rs
paramlist ::= lt*:vec(<labelvaltype>) => (param lt)*
resultlist ::= 0x00 t:<valtype> => (result t)
| 0x01 0x00 =>
Expand Down Expand Up @@ -386,7 +390,10 @@ Notes:
export, respectively, in the same scope (component, component type or
instance type).
* Validation of `[constructor]` names requires that the `func` returns a
`(result (own $R))`, where `$R` is the resource labeled `r`.
`(result (own $R))`, where `$R` is the resource labeled `r`. Additionally,
the function type *should* have the `non-blocking` attribute. In a future
pre-1.0 breaking change, validation will *require* the `non-blocking`
attribute.
* Validation of `[method]` names requires the first parameter of the function
to be `(param "self" (borrow $R))`, where `$R` is the resource labeled `r`.
* Validation of `[method]` and `[static]` names ensures that all field names
Expand Down Expand Up @@ -487,6 +494,8 @@ named once.
* The opcodes (for types, canon built-ins, etc) should be re-sorted
* The two `list` type codes should be merged into one with an optional immediate.
* The `0x00` prefix byte of `importname'` and `exportname'` will be removed or repurposed.
* Merge the two `functype` opcodes.
* Require `non-blocking` on `[constructor]` functions.


[`core:byte`]: https://webassembly.github.io/spec/core/binary/values.html#binary-byte
Expand Down
13 changes: 9 additions & 4 deletions design/mvp/Explainer.md
Original file line number Diff line number Diff line change
Expand Up @@ -555,12 +555,13 @@ defvaltype ::= bool
| (result <valtype>? (error <valtype>)?)
| (own <typeidx>)
| (borrow <typeidx>)
| (stream <typeidx>?)
| (future <typeidx>?)
| (stream <typeidx>?) 🔀
| (future <typeidx>?) 🔀
valtype ::= <typeidx>
| <defvaltype>
resourcetype ::= (resource (rep i32) (dtor async? <funcidx> (callback <funcidx>)?)?)
functype ::= (func (param "<label>" <valtype>)* (result <valtype>)?)
functype ::= (func <funcattr>? (param "<label>" <valtype>)* (result <valtype>)?)
funcattr ::= non-blocking 🔀
componenttype ::= (component <componentdecl>*)
instancetype ::= (instance <instancedecl>*)
componentdecl ::= <importdecl>
Expand Down Expand Up @@ -783,7 +784,11 @@ shared-nothing functions, resources, components, and component instances:

The `func` type constructor describes a component-level function definition
that takes a list of uniquely-named `valtype` parameters and optionally returns
a `valtype`.
a `valtype`. Function types can optionally be annotated with a `non-blocking`
attribute which has no semantic effect and is ignored at validation- and
run-time, serving primarily as a hint that tells bindings generators to lift
and lower without setting the `async` `canonopt` (see the [async
explainer](Async.md#sync-and-async-functions) for more details).

The `resource` type constructor creates a fresh type for each instance of the
containing component (with "freshness" and its interaction with general
Expand Down
3 changes: 2 additions & 1 deletion design/mvp/WIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,7 @@ keyword ::= 'as'
| 'include'
| 'interface'
| 'list'
| 'non-blocking'
| 'option'
| 'own'
| 'package'
Expand Down Expand Up @@ -1295,7 +1296,7 @@ typedef-item ::= resource-item
func-item ::= id ':' func-type ';'
func-type ::= 'func' param-list result-list
func-type ::= 'non-blocking'? 'func' param-list result-list
param-list ::= '(' named-type-list ')'
Expand Down

0 comments on commit d1db043

Please sign in to comment.