Skip to content

Commit 8e1e70a

Browse files
authored
Make {stream,future}.new return both a readable and writable end (#497)
Resolves #475
1 parent d779c42 commit 8e1e70a

File tree

5 files changed

+219
-265
lines changed

5 files changed

+219
-265
lines changed

design/mvp/Async.md

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -318,13 +318,10 @@ a `stream` or `future` somewhere in the parameter types) or result (of an
318318
import call with a `stream` or `future` somewhere in the result type), the
319319
receiver always gets *unique ownership* of the *readable end* of the `stream`
320320
or `future`. When *producing* a `stream` or `future` value as a parameter (of
321-
an import call) or result (of an export call), the producer can either
322-
*transfer ownership* of a readable end it has already received or it can create
323-
a fresh writable end (via `stream.new` or `future.new`) and then lift this
324-
writable end to create a fresh readable end in the consumer while maintaining
325-
ownership of the writable end in the producer. To maintain the invariant that
326-
readable ends are unique, a writable end can be lifted at most once, trapping
327-
otherwise.
321+
an import call) or result (of an export call), the producer can *transfer
322+
ownership* of a readable end that it has either been given by the outside world
323+
or freshly created via `{stream,future}.new` (which also return a fresh paired
324+
writable end that is permanently owned by the calling component instance).
328325

329326
Based on this, `stream<T>` and `future<T>` values can be passed between
330327
functions as if they were synchronous `list<T>` and `T` values, resp. For
@@ -339,16 +336,6 @@ its returned `stream<T>` with an [`error-context`](Explainer.md#error-context-ty
339336
value which `g` will receive along with the notification that its readable
340337
stream was closed.
341338

342-
If a component instance *would* receive the readable end of a stream for which
343-
it already owns the writable end, the readable end disappears and the existing
344-
writable end is received instead (since the guest can now handle the whole
345-
stream more efficiently wholly from within guest code). E.g., if the same
346-
component instance defined `f` and `g` above, the composition `g(f(x))` would
347-
just instruct the guest to stream directly from `f` into `g` without crossing a
348-
component boundary or performing any extra copies. Thus, strengthening the
349-
previously-mentioned invariant, the readable and writable ends of a stream are
350-
unique *and never in the same component*.
351-
352339
Given the readable or writable end of a stream, core wasm code can call the
353340
imported `stream.read` or `stream.write` canonical built-ins, resp., passing the
354341
pointer and length of a linear-memory buffer to write-into or read-from, resp.
@@ -358,6 +345,10 @@ value indicating that the read or write will execute concurrently. The readable
358345
and writable ends of streams and futures can then be [waited](#waiting) on to
359346
make progress.
360347

348+
As a temporary limitation, if a `read` and `write` for a single stream or
349+
future occur from within the same component, there is a trap. In the future
350+
this limitation will be removed.
351+
361352
The `T` element type of streams and futures is optional, such that `future` and
362353
`stream` can be written in WIT without a trailing `<T>`. In this case, the
363354
asynchronous "values(s)" being delivered are effectively meaningless [unit]
@@ -369,14 +360,6 @@ without requiring an explicit `future` return type. Thus, a function like
369360
which point the caller receives the readable end of a `future` that, when
370361
successfully read, conveys the completion of a second event.
371362

372-
From a [structured-concurrency](#structured-concurrency) perspective, the
373-
readable and writable ends of streams and futures are leaves of the async call
374-
tree. Unlike subtasks, the parent of the readable ends of streams and future
375-
*can* change over time (when transferred via function call, as mentioned
376-
above). However, there is always *some* parent `Task` and this parent `Task`
377-
is prevented from orphaning its children using the same reference-counting
378-
guard mentioned above for subtasks.
379-
380363
The [Stream State] and [Future State] sections describe the runtime state
381364
maintained for streams and futures by the Canonical ABI.
382365

@@ -928,6 +911,8 @@ Native async support is being proposed incrementally. The following features
928911
will be added in future chunks roughly in the order listed to complete the full
929912
"async" story, with a TBD cutoff between what's in [WASI Preview 3] and what
930913
comes after:
914+
* remove the temporary trap mentioned above that occurs when a `read` and
915+
`write` of a stream/future happen from within the same component instance
931916
* `subtask.cancel`: allow a supertask to signal to a subtask that its result is
932917
no longer wanted and to please wrap it up promptly
933918
* zero-copy forwarding/splicing

0 commit comments

Comments
 (0)