From 2cb9612ba16e82d6b04e34359f0b825ce8449ee5 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 17 Jan 2025 14:16:10 -0600 Subject: [PATCH 1/2] Add 'async' hint to functions --- design/mvp/Async.md | 39 +++++++++++++++++++++++++++++++++++---- design/mvp/Explainer.md | 31 +++++++++++++++++++++---------- design/mvp/WIT.md | 19 +++++++++++++++++-- 3 files changed, 73 insertions(+), 16 deletions(-) diff --git a/design/mvp/Async.md b/design/mvp/Async.md index f80fb4ca..a09de47b 100644 --- a/design/mvp/Async.md +++ b/design/mvp/Async.md @@ -154,7 +154,40 @@ 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. + +Functions *may* however be annotated (in both WIT and component binaries) with +`async` as a *hint*. This hint is intended to inform the default language +bindings generation, indicating whether to use a source-level `async` function +or not in languages that have such a distinction (e.g., JS, Python, C# and +Rust). In the absence of such a hint, a bindings generator would be forced to +make a uniform decision for what to do by default for all functions or require +manual programmer directives. However, because `async` is just a hint, there is +no prohibition against non-`async`-exported functions calling imported `async` +functions. This does mean that non-`async` functions may end up blocking +their caller, but (1) any loss in performance is the callee's "fault", (2) the +caller can still lower `async` if they want to (overriding the default hint), +(3) any *transitive* caller an lower `async` to avoid blocking. + +For example, given this interface: +```wit +interface filesystem { + resource file { + constructor(); + is-closed: func() -> bool; + read: async func(num-bytes: u32) -> result>; + from-bytes: static func(bytes: list) -> file; + from-stream: static async func(bytes: stream) -> file; + } +} +``` +a bindings generator in a language with `async` would only emit `async` +functions for `read` and `fetch`. Since in many languages `new` expressions +cannot be async, there is no `async constructor`. Use cases requiring +asynchronous construction can instead use `static async` functions, similar to +`from-stream` in this example. + ### Task @@ -709,11 +742,9 @@ time as applications are built with more components. ## TODO 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 +will be added in future chunks roughly in the order listed 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 * `subtask.cancel`: allow a supertask to signal to a subtask that its result is no longer wanted and to please wrap it up promptly * zero-copy forwarding/splicing diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md index b34146b8..f2b992ad 100644 --- a/design/mvp/Explainer.md +++ b/design/mvp/Explainer.md @@ -2191,9 +2191,12 @@ importname ::= | | plainname ::=