@@ -19,6 +19,7 @@ summary of the motivation and animated sketch of the design in action.
1919 * [ Structured concurrency] ( #structured-concurrency )
2020 * [ Streams and Futures] ( #streams-and-futures )
2121 * [ Waiting] ( #waiting )
22+ * [ Readiness] ( #readiness )
2223 * [ Backpressure] ( #backpressure )
2324 * [ Returning] ( #returning )
2425 * [ Borrows] ( #borrows )
@@ -459,6 +460,60 @@ tasks to be scheduled before continuing execution of the current task. A task
459460can yield by either calling [ ` yield ` ] or, when using a ` callback ` , by returning
460461the Canonical-ABI-defined "yield" code to the event loop.
461462
463+ ### Readiness
464+
465+ When passed a non-zero-length buffer, the ` stream.read ` and ` stream.write `
466+ built-ins are "completion-based" (in the style of, e.g., [ Overlapped I/O] or
467+ [ ` io_uring ` ] ) in that they complete only once one or more values have been
468+ copied to or from the memory buffer passed into the operation. Completion-based
469+ I/O avoids intermediate copies and enables a greater degree of concurrency in a
470+ number of cases and thus language producer toolchains should attempt to pass
471+ non-zero-length buffers whenever possible.
472+
473+ Given completion-based ` stream.{read,write} ` built-ins, "readiness-based" APIs
474+ (e.g., [ ` select ` ] or [ ` epoll ` ] ) can be implemented (e.g., by [ wasi-libc] ) by
475+ passing an intermedaite non-zero-length memory buffer to ` stream.{read,write} `
476+ and signalling readiness once the operation completes. However, this approach
477+ incurs extra copying overhead. To avoid this overhead in a best-effort mannner,
478+ ` stream.{read,write} ` additionally allow the given buffer length to be zero in
479+ which case "completion" of the ` stream.{read,write} ` is allowed (but not
480+ required) to wait to complete until the other end is "ready". As the "but not
481+ required" caveat suggests, after a zero-length read or write succeeds, there is
482+ no guarantee that the next non-zero-length ` stream.{read,write} ` will complete
483+ without blocking (due to any number of practical externalities or because
484+ readiness was simply not possible to implement given an underlying host API).
485+ Thus, a robust implementation of a guest readiness-based API using zero-length
486+ ` stream.{read,write} ` must fall back to non-zero-length buffering.
487+
488+ TODO...
489+
490+ As an example, ` select() ` could be implemented in wasi-libc with the following
491+ rough implementation strategy
492+ * When ` select() ` ing a file descriptor, a zero-length read or write is started
493+ (if one is not already pending) and the stream is added to the waitable set
494+ waited on by ` select() ` .
495+ * When the zero-length read or write completes, ` select() ` marks the stream's
496+ file descriptor as "ready" and remembers that zero-length read/write said so.
497+
498+ Based on this rule, to implement a traditional
499+ ` O_NONBLOCK ` ` write() ` or ` sendmsg() ` API, a writer can use a buffering scheme
500+ in which, after ` select() ` (or a similar API) signals a file descriptor is
501+ ready to write, the next ` O_NONBLOCK ` ` write() ` /` sendmsg() ` on that file
502+ descriptor copies to an internal buffer and suceeds, issuing an ` async `
503+ ` stream.write ` in the background and waiting for completion before signalling
504+ readiness again. Note that buffering only occurs when streaming between two
505+ components using non-blocking I/O; if either side is the host or a component
506+ using blocking or completion-based I/O, no buffering is necessary. This
507+ buffering is analogous to the buffering performed in kernel memory by a
508+ ` pipe() ` .
509+
510+
511+ * why not * require* readiness... constraints host, necessary for c2c
512+ * specifically c2c both-readiness case
513+ * trouble, but mostly only wasi-libc
514+ * only overhead in some cases
515+
516+
462517### Backpressure
463518
464519Once a component exports functions using the async ABI, multiple concurrent
@@ -1134,6 +1189,10 @@ comes after:
11341189[ FS or GS Segment Base Address ] : https://docs.kernel.org/arch/x86/x86_64/fsgs.html
11351190[ Cooperative ] : https://en.wikipedia.org/wiki/Cooperative_multitasking
11361191[ Multithreading ] : https://en.wikipedia.org/wiki/Multithreading_(computer_architecture)
1192+ [ Overlapped I/O ] : https://en.wikipedia.org/wiki/Overlapped_I/O
1193+ [ `io_uring` ] : https://en.wikipedia.org/wiki/Io_uring
1194+ [ `select` ] : https://pubs.opengroup.org/onlinepubs/007908799/xsh/select.html
1195+ [ `epoll` ] : https://en.wikipedia.org/wiki/Epoll
11371196
11381197[ AST Explainer ] : Explainer.md
11391198[ Lift and Lower Definitions ] : Explainer.md#canonical-definitions
@@ -1190,6 +1249,7 @@ comes after:
11901249[ shared-everything-threads ] : https://github.com/webAssembly/shared-everything-threads
11911250[ memory64 ] : https://github.com/webAssembly/memory64
11921251[ wasm-gc ] : https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md
1252+ [ wasi-libc ] : https://github.com/WebAssembly/wasi-libc
11931253
11941254[ WASI Preview 3 ] : https://github.com/WebAssembly/WASI/tree/main/wasip2#looking-forward-to-preview-3
11951255[ `wasi:http/handler.handle` ] : https://github.com/WebAssembly/wasi-http/blob/main/wit-0.3.0-draft/handler.wit
0 commit comments