Skip to content

embedder: enforce stream operation and transfer lifetimes - #335

Merged
lannbot merged 1 commit into
mainfrom
fix/stream-facade-lifecycle
Sep 11, 2026
Merged

embedder: enforce stream operation and transfer lifetimes#335
lannbot merged 1 commit into
mainfrom
fix/stream-facade-lifecycle

Conversation

@lannbot

@lannbot lannbot commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #324. Fixes #326. Fixes #327. Fixes #331.

  • Reserve writer operations synchronously, including before binding; cancellation retracts the intended operation without stale continuations or retained per-operation bind waiters.
  • Make unbound stream disposal terminal and settle queued work promptly.
  • Refuse busy readable transfers before guest entry, including direct-session reissue gaps, aliases, and deferred Future adoption. Keep completed Future payloads single-use while preserving memoized awaits.
  • Throw busy-operation TypeErrors synchronously; retain Promise rejection for other read errors. Preserve progress and exactly-once resource cleanup, including reentrant cancellation and throwing cleanup.
  • Add real-Wasm transfer regressions and focused lifecycle/error-channel coverage.

Semantic review

Authority: Component Model revision 7c676115e93cd7d54c1732d95c54c6a3de7c5ae0, definitions.py functions lift_async_value, stream_copy, future_copy, cancel_copy, and CopyEnd; contracts/embedder-api.md Streams and futures; architecture §§5–7.

lift_async_value requires IDLE; stream completion returns to IDLE, while a completed Future becomes DONE. The reference host lower_stream/lower_future helpers construct fresh endpoints without a busy guard, so the JS facade must preserve readable ownership/exclusion before handing values to them. This implements the existing facade contract without changing it or the protocol goldens.

Wasmtime source sanity check at pinned 4675ee16b703b33948073a5ff6b961367371e7a1: handle_table.rs busy removal checks; futures_and_streams.rs lift_index_to_transmit, consuming StreamReader::pipe, and stream_close invalidation.

Actual native component probes ran on installed Wasmtime 48.0.1 (7bac2c277), distinct from the source pin. A focused busy-readable WAST plus the pinned corpus cancel-stream.wast, closed-stream.wast, and trap-if-done.wast passed using wasmtime wast -W component-model-async=y,component-model-async-stackful=y. They confirm busy transfer rejection, eager/async completed-future rejection, cancel-and-reuse, and reader-drop → writer DROPPED. JS lazy binding and synchronous Promise error timing have no direct equivalent in those native APIs.

Verification

  • Fresh original-HEAD regression run failed on writer reservation and immediate cancellation (later cases canceled following its uncaught asynchronous busy error).
  • Focused stream/future tests passed with all fixtures present; independent final review: LGTM, 49 lifecycle/passthrough/producer tests passed.
  • just gates < /dev/null passed through core/runtime, packaging, conformance, seeded scheduling and shell recipes; its first browser launch required installing this worktree's Playwright browsers. just browsers smoke-tls smoke-c0 < /dev/null then passed (exit 0), completing all recipes. Consumer git status unchanged.
  • Formatting, lint, typecheck and git diff --check passed.

Auto-merge will be armed after opening; required core checks must pass.

@lannbot
lannbot enabled auto-merge September 11, 2026 03:15
@lannbot
lannbot merged commit 332cb3c into main Sep 11, 2026
4 checks passed
@lannbot
lannbot deleted the fix/stream-facade-lifecycle branch September 11, 2026 03:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment