Remix.run Logo
derefr an hour ago

> WASI should be ... stable.

The WASI standard is not at 1.0 yet! The people designing WASI are still trying to figure out what people want WASI to be at this point.

This is very likely to involve a lot of major reworking before 1.0, in response to feedback from orgs actually trying to implement WASI-based WebAssembly embeddings into their systems and runtimes. 0.1.x -> 0.2.x was one reworking; 0.2.x -> 0.3.x is another. There may be more of these before an approach is finally settled upon / "locked in" for 1.x.

---

> Let's keep WebAssembly lean and fast!

AFAICT, the entire point of the changes (incl. the more detailed component model) in WASI 0.3 is performance. Not performance of WebAssembly as a black box, though; but rather, performance of the running system as a whole, when a lot of FFI traffic is flowing across the WASI boundary. The richer component model enables lower impedance mismatches and "thinner" FFI-layer implementations.

For example, from the OP:

> WASI 0.2 handed you an output-stream that you wrote into imperatively. WASI 0.3 has you pass in a stream<u8> and get back a future that resolves when the write completes.

For some host languages/runtimes, "imperative blocking write calls" is already how writes against IO descriptors are exposed to the programmer. For those languages, WASI 0.2 made sense.

But in other host languages/runtimes, writes against IO descriptors are inherently non-blocking, returning promises or yielding. For those languages, WASI 0.2 "left performance on the table." WASI 0.2 required such languages to implement a blocking IO write abstraction on top of their non-blocking IO write semantics, in order to pass that blocking-IO-write primitive into the WebAssembly componennt... even if the WebAssembly component was internally concurrent (e.g. compiled from a language like Golang) and so would highly benefit from a non-blocking-IO-write primitive!

Meanwhile, if you require that the host expose a non-blocking-IO-write primitive (as WASI 0.3 does), then for hosts with native non-blocking IO, doing so is free; while for hosts with only blocking IO, non-blocking IO can be "faked" basically for free (i.e. with a global or per-resource linearized write queue on the host side.) And likewise, non-blocking-IO-aware WebAssembly components can freely take advantage of NIO; while WebAssembly components that expect blocking IO only need the tiniest added bit of a codegen shim (`blocking_write(x) => await nonblocking_write(x);`) to fit into a WASI 0.3 world.

In other words, implementing nonblocking IO abstractions on top of blocking IO abstractions costs FFI performance, but implementing blocking IO abstractions on top of nonblocking IO abstractions is "free" (in FFI terms.) Nonblocking IO should therefore be considered the more "primitive" of the two; and so, if you have to choose only BIO or NIO to expose as a capability across a boundary to an unknown peer, NIO should be the one you choose.

---

That being said...

The WASI devs were likely aware of the "FFI optimization opportunities being left on the floor" in WASI 0.2. They likely already wanted to take things in this direction from the beginning. But in WASI 0.2, without async, it was impossible to express the concept of nonblocking IO (i.e. of IO operations returning a promise/future.) They needed to introduce this more "opinionated" (i.e. richer) component model in order to get here.

AFAICT, WASI 0.2 was never intended to be a Release Candidate of the WASI spec. (And WASI 0.3 likely isn't either!)

Rather, WASI 0.2 had areas (like IO) that were purposefully left "under-baked". The WASI team knew people needed some version of these primitives in order for WebAssembly components to usefully integrate into systems at all. But they hadn't yet put in the work on designing how certain aspects of WASI (e.g. async) would work. So they designed WASI 0.2 as a prototype design, based on the limited toolbag of primitives they had already fully agreed upon. Some aspects of WASI turned out to only "want" that limited toolbag of primitives, and so didn't change at all under WASI 0.3 (and might even be in their final shapes.) Other aspects "wanted" things that weren't there, and so experienced over-constrained designs under WASI 0.2, replaced with less-constrained designs under WASI 0.3.

I fully expect there will be more such changes under WASI 0.4.