WebAssembly / WebAssembly/component-model

stream.forward/splice sketch

Open
#658 11 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
WebAssembly
Stars
1.4k
Forks
130
Avg merge
2d 1h
Merged PRs (30d)
15

Description

(Filing this issue to sketch a post-0.3.0 follow-up addition, for whenever someone hits the use cases and is ready to implement.)

Currently, streams can be zero-cost forwarded across component boundaries via the stream<T> type, where the producer passes the i32 index of a readable stream end in the producer's handle table, and then this readable end is transferred to the consumer. However, this still leaves some cases where we'd like to do zero-cost forwarding, but can't:

  • when the function taking the stream<T> needs to be called before the caller has received the readable end that they'd like to forward from
  • when there's a partial copy
  • when there's a need to find out after the forwarding is finished

These cases will end up requiring:

  1. a stream.new to create a fresh (readable, writable) end pair
  2. passing out the fresh readable end
  3. copying into the fresh writable end from some other readable end at some point in the future

Steps 1 and 2 are fine/cheap, but ideally we'd avoid the O(n) copy in step 3. To do this, I was thinking there could be a new built-in definition:

(canon stream.splice $streamT async? (core func $stream.splice))

where $stream.splice is given type:

(func $stream.splice (param $readable-end i32) (param $writable-end i32) (param $n i32) (result i32))

where $readable-end and $writable-end logically borrow the given readable/writable ends for the duration of the $stream.splice. The return value would then either be a sentinel "blocked" (-1) value (if async was set) or else the synchronous result of the copy. In the "blocked" case, until the splicing completes, both the readable and writable ends of the stream would be in the "copying" state (so that they couldn't be dropped or separately read/written). To avoid ambiguous event delivery, only one end (e.g., maybe just the writing end) could be put in a waitable-set or stream.canceled. Care would need to be taken when delivering the "result" to unambiguously deliver drops that occurred on either end during the splicing operation.

While stream.splice addresses the general case (in particular the 2nd and 3rd bulleted use cases above), in some cases (like the first bulleted use case), "everything" is forwarded and the caller wants "dropping" to propagate in both directions in the obvious way. To optimize this common case, there could be a second, simplified built-in:

(canon stream.forward $streamT (core func $stream.forward))

where $stream.forward is given type:

(func $stream.forward (param $readable-end i32) (param $writable-end i32))

where $readable-end and $writable-end are transferred out of the calling component instance's handle table to the host, effectively cutting the calling component instance out of the stream chain, thereby allowing the calling component instance to be more-eagerly torn down by the runtime (than with stream.splice), which is an important optimization in various proxy scenarios.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No files or tests are named. Start by reading the proposed stream.splice and stream.forward definitions and their core function signatures in this issue. Done would require an agreed design for the borrowing, blocking, transfer, cancellation, and drop semantics before specification work can begin.

Written by the indexing model from the issue text.

Assessment

Tech stack
wasm
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.