Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'non-blocking' function attribute #442

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a new Wit user perspective. the name "non-blocking" could sound like it means "no stopping the caller" rather than "no waiting for I/O", which would be confusing since it does the exact opposite of that :-}. At least we should clearly document the Wit-user-facing side of this keyword.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I suppose "blocking" does have multiple interpretations. I added this addition to the previous note, but it could probably be improved.



### 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the "primarily" here redundant?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I suppose so. It felt a bit off without any word, so I tried "only", and also tightened up the rest of the sentence in this commit, PTAL

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some text to Wit.md briefly describing what non-blocking does, from a Wit user perspective? And maybe link back to the Explainer.md for the full details?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, fixed, PTAL

func-type ::= 'non-blocking'? 'func' param-list result-list
param-list ::= '(' named-type-list ')'
Expand Down
Loading