Skip to content

Commit 3bf1a2f

Browse files
authored
Merge pull request #123 from lann/wasi-sockets-tcp
wasi-shims: client TCP for the à la carte wasi:sockets fragment; embedder A12 (future-typed import results)
2 parents 8fb4d7f + 0b23be4 commit 3bf1a2f

16 files changed

Lines changed: 1476 additions & 55 deletions

File tree

contracts/embedder-api.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ host-import settlements are serviced by a settlement pump while no export
4747
call is in flight, so background tasks parked on host-call wakeups (clocks,
4848
fetches) progress without embedder traffic — embedder-never-acts operations
4949
still hang (never trap) and failures still surface on the next driving
50-
call.**
50+
call; amendment A12 (2026-08-12) makes result-position future sources
51+
normative for imports: an import whose WIT result type is `future<T>`
52+
treats a thenable return as the FUTURE SOURCE (the import completes
53+
immediately; the future settles on the producer's schedule) — see
54+
§"Streams and futures".**
5155
This document supersedes `descriptor-ir.md`'s interim
5256
"host value mapping" table as the destination for host-facing value shapes.
5357
The runtime's *raw* boundary (`instance.exports`, `HostImports`) keeps the
@@ -469,6 +473,26 @@ class DroppedError extends Error { … } // awaiting a dropped future rejects
469473
deadlock-masking activity-lifetime footgun — R-fix review note 2), and
470474
cross-store reuse is a runtime-asserted error, not silent misbehavior
471475
(note 3).
476+
- **An import whose WIT result type is `future<T>` returns the future
477+
source** (amendment A12, 2026-08-12). A thenable returned by the host
478+
method — a `Promise<T>` or a `Future<T>` handle — is lowered as the
479+
future itself: the import call completes immediately, and the future
480+
settles on the producer's schedule. It is **not** adopted as the call's
481+
async completion (the pre-A12 dispatch behavior, under which a sync-typed
482+
import returning a Promise was a JSPI park request — and under which a
483+
`Future` handle, being `PromiseLike`, was silently awaited and
484+
re-lowered). The natural spelling of the `wasi:sockets@0.3` TCP `send`
485+
shape — `send: func(data: stream<u8>) -> future<result<_, error-code>>`
486+
as an `async` JS method whose promise resolves when transmission
487+
completes — depends on this: the future settles only after post-return
488+
guest action (the guest writes `data` after `send` returns), so adopting
489+
the thenable is a livelock, not a semantics choice. A **rejected**
490+
future-source promise stays a producer failure on the host-failure
491+
channel (site-named, surfacing on the consuming call — same as every
492+
producer), never a guest-visible err value: a fallible payload rides
493+
*inside* the future (`future<result<…>>`), resolved as a result value.
494+
Executable spec: `examples/guests/future-import` +
495+
`runtime/tests/embedder/future_result_test.ts`.
472496
- **Stream values survive round trips** (amendment A5). A `stream`/`future`
473497
is an identity: lifting one that the host already handled — a
474498
host-created stream a guest passed back (result or import position), or

docs/architecture.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,9 @@ contracts throughout the repo. Related documents:
8888
they are the ecosystem's most important interfaces and the conventions must
8989
serve them well — and a minimal WASI shim *package* (`wasi-shims/`, a
9090
separate deliverable; consumer-driven scope: p2 cli/io/clocks/random
91-
baseline + p3 clocks, plus an à la carte p3 `wasi:sockets` UDP fragment
92-
(`@deltic/wasi-shims/sockets`, adopted from polymorph-iroh's Deno host —
91+
baseline + p3 clocks, plus an à la carte p3 `wasi:sockets` fragment
92+
(`@deltic/wasi-shims/sockets` — UDP adopted from polymorph-iroh's Deno
93+
host, client TCP for the wosh consumer;
9394
[#4](https://github.com/lann/deltic/issues/4)) that Deno-native hosts
9495
opt into and the default `wasiShims()` never carries).
9596
- **Componentizing JS/TS.** Guests are components built by external toolchains

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ WASI imports** — so componentization needs no wasip1 adapter.
3737
| `backpressure-probe.component.wasm` | [`guests/backpressure-probe/wit/world.wit`](guests/backpressure-probe/wit/world.wit) | `toggle-around-yield: async func(x: u32) -> u32` (asserts backpressure, yields, clears backpressure) | ~40 KB |
3838
| `stream-echo.component.wasm` | [`guests/stream-echo/wit/world.wit`](guests/stream-echo/wit/world.wit) | `echo-doubled: async func(input: stream<u32>) -> stream<u32>` — consumes AND produces a stream in one export | ~60 KB |
3939
| `future-user.component.wasm` | [`guests/future-user/wit/world.wit`](guests/future-user/wit/world.wit) | `double-future: async func(f: future<u32>) -> u32` (awaits an imported future); `make-future: async func(x: u32) -> future<u32>` (resolves an exported one) | ~64 KB |
40+
| `future-import.component.wasm` | [`guests/future-import/wit/world.wit`](guests/future-import/wit/world.wit) | Host imports with future-bearing results (amendment A12; the `wasi:sockets@0.3` TCP shapes reduced to `u32`): `next-value: func() -> future<u32>`, `send-sink: func(stream<u8>) -> future<u32>`, `recv-pair: func() -> tuple<stream<u8>, future<u32>>`, driven by `run-next`/`run-send`/`run-recv` exports (`run-send` writes the stream only after the sync import returns — the livelock probe) | ~64 KB |
4041
| `test-suite.component.wasm` | [`guests/test-suite/wit/tests.wit`](guests/test-suite/wit/tests.wit) (vendored verbatim from polymorph-test's `polymorph:test@0.1.0`, TRACK C2-D) | Implements the `suite` world: imports `test-context`, exports `tests` (`all: async func() -> list<test-case>`). Six deterministic cases exercising pass/fail/skip, multi-message diagnostics, and a measurable-time case for budget plumbing — the ct-runner's (`../../ct-runner/`) fixture. | ~61 KB |
4142

4243

examples/build.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@ TARGET=wasm32-unknown-unknown
2020
BUILD_DIR=guests/build
2121
export CARGO_TARGET_DIR="$PWD/guests/target"
2222

23-
GUESTS="hello values resources async-probe yield-only context-user backpressure-probe stream-echo stream-pass future-user test-suite"
23+
GUESTS="hello values resources async-probe yield-only context-user backpressure-probe stream-echo stream-pass future-user future-import test-suite"
2424

2525
# wasm-tools validation features per guest (component-model always on;
2626
# CM 0.3 async guests additionally need the cm-async feature).
2727
features_for() {
2828
case "$1" in
29-
async-probe|yield-only|context-user|backpressure-probe|stream-echo|stream-pass|future-user|test-suite)
29+
async-probe|yield-only|context-user|backpressure-probe|stream-echo|stream-pass|future-user|future-import|test-suite)
3030
echo "component-model,cm-async" ;;
3131
*) echo "component-model" ;;
3232
esac

0 commit comments

Comments
 (0)