Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion experiments/iroh-blobs/host/browser-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ try {
);
const summary =
`[driver] done in ${((performance.now() - t0) / 1000).toFixed(1)}s; ` +
`polls=${stats.pollCalls} (suspended ${stats.pollSuspends}) ` +
`datagrams in/out=${stats.datagramsIn}/${stats.datagramsOut} ` +
`ws connections=${bridgeStats.connections} ws msgs in/out=${bridgeStats.wsIn}/${bridgeStats.wsOut} ` +
`webrtc channels=${webrtcStats.channelsOpened} msgs in/out=${webrtcStats.in}/${webrtcStats.out} ` +
Expand Down
1 change: 0 additions & 1 deletion experiments/iroh-blobs/host/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ clearTimeout(watchdog);

console.log(
`[driver] done in ${((performance.now() - t0) / 1000).toFixed(1)}s; ` +
`polls=${stats.pollCalls} (suspended ${stats.pollSuspends}) ` +
`datagrams in/out=${stats.datagramsIn}/${stats.datagramsOut} ` +
`ws connections=${bridgeStats.connections} ws msgs in/out=${bridgeStats.wsIn}/${bridgeStats.wsOut} ` +
`webrtc channels=${webrtcStats.channelsOpened} msgs in/out=${webrtcStats.in}/${webrtcStats.out} ` +
Expand Down
1 change: 0 additions & 1 deletion experiments/iroh-relay-ws/host/browser-entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ try {
);
const summary =
`[driver] done in ${((performance.now() - t0) / 1000).toFixed(1)}s; ` +
`polls=${stats.pollCalls} (suspended ${stats.pollSuspends}) ` +
`datagrams in/out=${stats.datagramsIn}/${stats.datagramsOut} ` +
`ws connections=${bridgeStats.connections} ws msgs in/out=${bridgeStats.wsIn}/${bridgeStats.wsOut} ` +
`webrtc channels=${webrtcStats.channelsOpened} msgs in/out=${webrtcStats.in}/${webrtcStats.out} ` +
Expand Down
8 changes: 4 additions & 4 deletions experiments/iroh-relay-ws/host/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
"lib": ["dom", "dom.iterable", "dom.asynciterable", "deno.ns"]
},
"imports": {
"@deltic/runtime/embedder": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/runtime/src/embedder/mod.ts",
"@deltic/runtime/plan": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/runtime/src/plan/mod.ts",
"@deltic/runtime/shim": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/runtime/src/shim/mod.ts",
"@deltic/wasi-shims": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/wasi-shims/src/mod.ts",
"@deltic/runtime/embedder": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/embedder/mod.ts",
"@deltic/runtime/plan": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/plan/mod.ts",
"@deltic/runtime/shim": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/shim/mod.ts",
"@deltic/wasi-shims": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/wasi-shims/src/mod.ts",
"node-datachannel": "npm:node-datachannel@0.32.3",
"node-datachannel/polyfill": "npm:node-datachannel@0.32.3/polyfill",
"werift": "npm:werift@0.22.2"
Expand Down
118 changes: 59 additions & 59 deletions experiments/iroh-relay-ws/host/deno.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions experiments/iroh-relay-ws/host/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ export interface GuestOptions {

/**
* The full import record for the spike guests: deltic's wasi-shims
* baseline, with the synthetic network fragment (sockets.ts) replacing
* the tier-(a) poll/clock stubs, and stdio routed to the console
* line-buffered.
* baseline (whose A5 parking kernel serves poll/clock suspension), the
* synthetic network fragment (sockets.ts), and stdio routed to the
* console line-buffered.
*/
export function guestImports(options: GuestOptions): Record<string, unknown> {
const stdout = new OutputStream(lineSink("guest-out", console.log));
Expand All @@ -69,8 +69,8 @@ export function guestImports(options: GuestOptions): Record<string, unknown> {
*
* `artifacts` is anything `instantiate` accepts (embedder-api A3): the
* translated `ComponentArtifacts`, or `{ componentBytes, translator }`
* for in-process translation. jspi mode is selected by the `suspending()`
* markers in sockets.ts; no explicit option is needed.
* for in-process translation. jspi mode is selected by wasi-shims' own
* `suspending()` markers (the A5 kernel); no explicit option is needed.
*/
export async function runGuest(
artifacts: ComponentArtifacts | {
Expand Down
1 change: 0 additions & 1 deletion experiments/iroh-relay-ws/host/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ clearTimeout(watchdog);

console.log(
`[driver] done in ${((performance.now() - t0) / 1000).toFixed(1)}s; ` +
`polls=${stats.pollCalls} (suspended ${stats.pollSuspends}) ` +
`datagrams in/out=${stats.datagramsIn}/${stats.datagramsOut} ` +
`ws connections=${bridgeStats.connections} ws msgs in/out=${bridgeStats.wsIn}/${bridgeStats.wsOut} ` +
`webrtc channels=${webrtcStats.channelsOpened} msgs in/out=${webrtcStats.in}/${webrtcStats.out} ` +
Expand Down
119 changes: 20 additions & 99 deletions experiments/iroh-relay-ws/host/sockets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,28 @@
// to host-side bridges by well-known port (bridge.ts, webrtc-bridge.ts)
// or by full synthetic destination address (the issue #26 overlay).
//
// deltic's own `wasi-shims` package deliberately stops at tier (a)/(b) of
// the embedder contract's p2 strategy: its pollables are always-ready
// stubs and its clock subscriptions never fire, which busy-loops a guest
// that genuinely parks. The iroh guests park constantly (tokio's reactor
// blocks in `wasi:io/poll#poll`), so this module takes the contract's
// tier (c): `poll` and `Pollable.block` are sync-typed WIT functions
// implemented as Promise-returning JS, declared with the `suspending()`
// marker (embedder-api amendment A1) — the marked import parks the
// calling wasm frame on JSPI, and the marker itself selects jspi mode.
// Parking is deltic's, not this module's: wasi-shims' kernel (embedder-api
// A5) serves `wasi:io/poll` and the clock subscriptions with real
// suspension, and its `Pollable` is publicly constructible as the interop
// seam — this module only mints `new Pollable(ready, wait)` over its
// datagram queues (the promise-swap wake pattern the kernel documents).
// Interfaces the guest links but can never use functionally (tcp,
// ip-name-lookup) are typed stubs that fail with WIT error values, not
// traps.
//
// Registered under compatibility-track keys (`@0.2`), so one provider
// serves whatever 0.2.x the guest binary names. Interfaces the guest
// links but can never use functionally (tcp, ip-name-lookup) are typed
// stubs that fail with WIT error values, not traps.
// serves whatever 0.2.x the guest binary names.
//
// Environment-portable: standard globals only (`performance`, `crypto`,
// `console`); works under Deno and in browsers.
// Environment-portable: standard globals only; works under Deno and in
// browsers.

import { suspending, WitError } from "@deltic/runtime/embedder";
import { WitError } from "@deltic/runtime/embedder";
import { Pollable } from "@deltic/wasi-shims";

// ---------------------------------------------------------------------------
// instrumentation

export const stats = {
pollCalls: 0,
pollSuspends: 0,
blocks: 0,
datagramsIn: 0, // bridge -> guest
datagramsOut: 0, // guest -> bridge
};
Expand Down Expand Up @@ -64,85 +59,14 @@ export interface OutgoingDatagram {
}

// ---------------------------------------------------------------------------
// wasi:io/poll — tier (c): pollables that really park

/** Nanosecond monotonic clock over `performance.now()`. */
const hrnow = (): bigint => BigInt(Math.round(performance.now() * 1e6));

export class Pollable {
#readyFn: () => boolean;
#waitFn: () => Promise<void>;
constructor(readyFn: () => boolean, waitFn: () => Promise<void>) {
this.#readyFn = readyFn;
this.#waitFn = waitFn;
}
ready(): boolean {
return this.#readyFn();
}
/** Sync WIT function that genuinely parks: tier (c), `@suspending`. */
@suspending
async block(): Promise<void> {
stats.blocks++;
while (!this.#readyFn()) await this.#waitFn();
}
waitPromise(): Promise<void> {
return this.#waitFn();
}
}

// pollable minting
//
// Every `own<pollable>` handed to the guest is a FRESH instance: two own
// handles sharing one host instance alias one registry rep, and the first
// guest-side drop would kill both (HostResourceRegistry semantics).
const ready = (): Pollable => new Pollable(() => true, () => Promise.resolve());
const never = (): Pollable => new Pollable(() => false, () => new Promise<void>(() => {}));

/** Duck-typed views of foreign pollables (deltic wasi-shims tier-(a) ones). */
interface PollableLike {
ready(): boolean;
waitPromise?: () => Promise<void>;
}

/**
* `wasi:io/poll#poll` — sync WIT, Promise-returning JS (tier (c)).
*
* The list mixes this module's pollables with foreign ones (deltic
* wasi-shims' stdio/filesystem pollables are always-ready stubs without
* `waitPromise`), so readiness is duck-typed: a foreign pollable that is
* not ready can never wake us, exactly like the NEVER pollable.
*/
async function poll(list: PollableLike[]): Promise<number[]> {
stats.pollCalls++;
for (;;) {
const ready: number[] = [];
for (let i = 0; i < list.length; i++) {
if (list[i].ready()) ready.push(i);
}
if (ready.length) return ready;
stats.pollSuspends++;
await Promise.race(list.map((p) => p.waitPromise?.() ?? new Promise<void>(() => {})));
}
}

// ---------------------------------------------------------------------------
// wasi:clocks/monotonic-clock — subscriptions that really fire

function timerPollable(deadlineNs: bigint): Pollable {
return new Pollable(
() => hrnow() >= deadlineNs,
() =>
new Promise((r) => {
const ms = Number(deadlineNs - hrnow()) / 1e6;
setTimeout(r, Math.max(0, ms));
}),
);
}

const monotonicClock = {
now: hrnow,
resolution: (): bigint => 1_000n,
subscribeInstant: (when: bigint): Pollable => timerPollable(when),
subscribeDuration: (ns: bigint): Pollable => timerPollable(hrnow() + ns),
};
const ready = (): Pollable => new Pollable();
const never = (): Pollable => new Pollable(() => false, () => new Promise<void>(() => {}));

// ---------------------------------------------------------------------------
// wasi:sockets — the synthetic network (multi-socket)
Expand Down Expand Up @@ -405,15 +329,12 @@ export class ResolveAddressStream {
// the import fragment

/**
* The synthetic-network provider fragment (track keys), spread AFTER
* deltic's `wasiShims(...)` so the tier-(c) `wasi:io/poll` and
* `wasi:clocks/monotonic-clock` implementations replace the stub tiers
* (same keys, object-spread override).
* The synthetic-network provider fragment (track keys), spread next to
* deltic's `wasiShims(...)`, whose A5 kernel serves `wasi:io/poll` and
* the clock subscriptions these sockets' pollables park under.
*/
export function syntheticNetImports(): Record<string, unknown> {
return {
"wasi:io/poll@0.2": { Pollable, poll: suspending(poll) },
"wasi:clocks/monotonic-clock@0.2": monotonicClock,
"wasi:sockets/network@0.2": { Network },
"wasi:sockets/instance-network@0.2": {
instanceNetwork: (): Network => theNetwork,
Expand Down
2 changes: 1 addition & 1 deletion experiments/ping-demo/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ shim=$(deno run --config ../../host-deltic/deno.json --frozen \
# repo's pinned tag; the raw URL below must carry the same tag (the pin
# gate in host-deltic/fetch-translator.ts checks it).
deno run --config "$SPIKE_HOST/deno.json" --allow-read --allow-write \
"https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/tools/translate/main.ts" \
"https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/tools/translate/main.ts" \
"$GUEST_WASM" -o "$SITE/ping-demo.plan.json" --shim "$shim"

cp "$GUEST_WASM" "$SITE/ping-demo.component.wasm"
Expand Down
4 changes: 3 additions & 1 deletion experiments/ping-demo/web/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,9 @@ try {
setStatus("error", `guest failed: ${err}`)
);
} catch (err) {
const jspi = typeof WebAssembly.Suspending === "function";
// Feature-probe only: deltic's jspi types are module-scoped (no global
// WebAssembly augmentation), so read the property untyped.
const jspi = typeof (WebAssembly as { Suspending?: unknown }).Suspending === "function";
setStatus(
"error",
jspi
Expand Down
6 changes: 3 additions & 3 deletions host-deltic/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"//": "MODULE-IDENTITY CONSTRAINT: deltic's wasi-shims and the sibling deltic host modules (.deps/{websocket,webrtc,webcrypto}) import @deltic/runtime/embedder by bare specifier internally; this file maps that specifier ONCE for the whole module graph, so `instanceof WitError` holds across every boundary. The npm mappings serve the webrtc module's bare `node-datachannel`/`werift` specifiers, which resolve against THIS config when run-endpoint.ts is the entry module.",
"nodeModulesDir": "auto",
"imports": {
"@deltic/runtime/embedder": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/runtime/src/embedder/mod.ts",
"@deltic/runtime/shim": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/runtime/src/shim/mod.ts",
"@deltic/wasi-shims": "https://raw.githubusercontent.com/lann/deltic/pre-eb3f8d0/wasi-shims/src/mod.ts",
"@deltic/runtime/embedder": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/embedder/mod.ts",
"@deltic/runtime/shim": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/runtime/src/shim/mod.ts",
"@deltic/wasi-shims": "https://raw.githubusercontent.com/lann/deltic/pre-a67ee83/wasi-shims/src/mod.ts",
"@polymorph/webcrypto-deltic": "../.deps/webcrypto/js/deltic/src/mod.ts",
"node-datachannel": "npm:node-datachannel@0.32.3",
"node-datachannel/polyfill": "npm:node-datachannel@0.32.3/polyfill",
Expand Down
Loading