WASI 0.3 is official, and async is now native to WebAssembly Components. The WASI Subgroup voted to ratify WASI 0.3.0, rebasing WASI onto the WebAssembly Component Model’s async primitives. The 0.3.0 specification is now stable, and runtime and toolchain support is landing now.
The work that wasi:io in WASI 0.2 used to do (pollables, input-streams, output-streams) is now part of the canonical ABI, where the Component Model now offers these primitives natively. As a concequence of that, most of the changes from WASI 0.2 to 0.3 are entirely mechanical and significantly simplify the signatures we had before. The new async primitives are part of the Component Model’s canonical ABI, enabling bindings generators to emit idiomatic async bindings for their given language.
The Component Model Async ABI
In WASI 0.2 each component needed its own event loop/async runtime. That meant that individual components could be run on a host, but there was no way for those event loops to coordinate with one another. If a component used streaming or async APIs, it couldn’t be composed with any other components.
WASI 0.3 makes it so the host is now the one in charge of managing the one event loop that is shared by all components. This is enabled by adding stream<T> , future<T> , and async as first-class constructs to the canonical ABI:
stream<T> and future<T> function like resource types: each is an owned handle, passing one across a component boundary transfers ownership from caller to callee. Unlike resource types, they can’t be borrowed.
and function like resource types: each is an owned handle, passing one across a component boundary transfers ownership from caller to callee. Unlike resource types, they can’t be borrowed. The runtime, not each component, drives the scheduling. When a value has been delivered to a future , the runtime schedules whichever task is awaiting it, even if it was passed through multiple component boundaries. The writer that delivers that value might be the host, another component, or even the same component that holds the read end.
, the runtime schedules whichever task is awaiting it, even if it was passed through multiple component boundaries. The writer that delivers that value might be the host, another component, or even the same component that holds the read end. The async model is completion-based, not readiness-based. This is similar to the ultra-efficient Linux io_uring and Windows’ IOCP/ IoRing APIs. An epoll / kqueue -style readiness API can be emulated on top of this for programs which need the compatibility.
and Windows’ IOCP/ APIs. An / -style readiness API can be emulated on top of this for programs which need the compatibility. Components export and import async func s directly. Gone is the three-step start-foo / finish-foo / subscribe dance from WASI 0.2.
Changes to the WASI interfaces
... continue reading