Skip to content

Slice 2 — Clock + Random providers#368

Merged
taybenlor merged 10 commits into
taybenlor:mainfrom
liverpoolie:wasix/slice-2-clock-random
Apr 29, 2026
Merged

Slice 2 — Clock + Random providers#368
taybenlor merged 10 commits into
taybenlor:mainfrom
liverpoolie:wasix/slice-2-clock-random

Conversation

@liverpoolie

Copy link
Copy Markdown
Contributor

Summary

Wires sync clock + random providers into WASIX per WASIX-PLAN.md:

  • WASIXError (wasix-32v1.ts) — providers throw it; the syscall layer translates to Result.*. Anything else thrown becomes EIO via the existing debug hook.
  • Four providersSystemClockProvider (performance.now() for MONOTONIC, Date.now() for the rest), SystemRandomProvider (crypto.getRandomValues), FixedClockProvider(epochNs, tick) (per-call advance, default 0n = frozen), SeededRandomProvider(seed) (SFC32 with 15 warm-up rounds — integer-only, engine-identical).
  • Three syscalls wiredwasix_32v1.clock_time_get, clock_res_get, random_get call through to the provider; lazy-initialised system defaults when no provider is supplied. Unknown ClockId returns EINVAL, not EIO.
  • ClockId enumREALTIME, MONOTONIC, PROCESS_CPUTIME, THREAD_CPUTIME.
  • Public surface — all four providers re-exported from lib/main.ts and attached to window for Playwright.
  • Smoke + determinism specswasix-clock-random.wat asserts MONOTONIC delta > 0 and non-zero random bytes; wasix-deterministic.wat hex-encodes 8 SFC32(seed=42) bytes + 8 bytes from FixedClock(0n,0n) to stdout. Spec asserts the literal golden b4b5424d031fbb710000000000000000 across two runs — pins the algorithm rather than just round-trip equality.
  • CIapt-get install wabt so wat2wasm is available in the PR test workflow.

Deviation note — hand-rolled WAT, not cargo wasix

Same substitution as Slice 1: cargo-wasix isn't installable in this repo's toolchain, so the smoke binaries are written in WAT and built via wat2wasm. The slice spec called out the cargo wasix path as a preference rather than a requirement.

Test plan

  • chromium / firefox / webkit green on the new wasix-clock-random.spec.ts (CI run at tip)
  • No regressions in existing args / stdio / wasix-smoke specs
  • tsc --noEmit clean
  • Determinism test pins exact bytes — algorithm drift in either provider would now flip the assertion

Follow-ups (already stacked on this branch)

  • Slice 3 — filesystem provider + wasmer test harness
  • Slice 4 — WASIXWorkerHost + Atomics bridge (carries COOP/COEP)

liverpoolie and others added 10 commits April 28, 2026 16:59
Providers can throw WASIXError(result) to return a specific errno;
any other thrown value becomes EIO via the debug hook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SystemClockProvider: Date.now() for REALTIME, performance.now() for
MONOTONIC; CPU clocks fall back to monotonic.
SystemRandomProvider: wraps crypto.getRandomValues in 64 KiB chunks.
FixedClockProvider: frozen or tick-advancing deterministic clock.
SeededRandomProvider: SFC32 PRNG, deterministic across all JS engines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces ENOSYS stubs with real implementations. Reads provider from
context; falls back to lazily-created SystemClockProvider /
SystemRandomProvider when context slot is unset, matching preview1
default behaviour.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ight

lib/main.ts exports SystemClockProvider, SystemRandomProvider,
FixedClockProvider, SeededRandomProvider from package root.
src/main.ts attaches them to window so Playwright specs can construct
providers in page.evaluate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
wasix-clock-random: calls clock_time_get(MONOTONIC) twice with a 5M-iter
spin between calls and asserts delta > 0; calls random_get(32) and
asserts non-zero. Exits 1 / 2 on failure, 0 on success.

wasix-deterministic: calls random_get(8) + clock_time_get(REALTIME) and
hex-encodes the 16 bytes for fd_write; Playwright runs it twice with
identical FixedClockProvider + SeededRandomProvider and asserts the
golden hex output.

Built fresh by `npm run build:tests` via wat2wasm; not committed.
cargo-wasix is unavailable here — see PR deviation note.
Slice 2 introduces hand-rolled WAT smoke binaries (wasix-clock-random,
wasix-deterministic). Chain wat2wasm onto the existing cargo step so
both the Rust .wasi.wasm artefacts (used by the existing args/stdio/
wasix-smoke specs) and the new .wasm artefacts get built by a single
`npm run build:tests` invocation. No swallow — if cargo is missing,
build:tests fails loudly.
wasix-clock-random.spec.ts: tests both binaries across chromium/firefox/
webkit. Clock-random asserts exit 0; deterministic asserts stdout is
byte-identical across two runs with FixedClockProvider + SeededRandomProvider.

package.json build:tests extended with wat2wasm calls for the two new
test binaries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both SystemClockProvider and FixedClockProvider were exhaustive at
compile time over ClockId but returned undefined at runtime for unknown
numeric ids — which the outer wasix_clock_time_get / wasix_clock_res_get
handlers surfaced as EIO. The WASIX and preview1 specs say unknown clock
ids should be EINVAL. Add a default arm throwing WASIXError(EINVAL) to
each now() / resolution() switch (four switches total) so the error
model matches the spec.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`packages/wasi`'s `build:tests:wat` step (added in this slice)
shells out to wat2wasm to compile the hand-written WAT smoke
binaries. The Actions runner image doesn't ship wabt by default,
so the step fails with `wat2wasm: command not found`.

apt installs wabt from Ubuntu's official repos.
The previous determinism check only asserted run1 === run2 and
non-empty stdout — silent algorithm drift that still produced
matching output across two runs would pass. Per review, pin the
exact byte sequence.

The WAT now hex-encodes the 16 bytes (8 random + 8 clock) into
32 ASCII chars before fd_write, so the bytes survive TextDecoder
intact. The spec asserts the literal golden:
`b4b5424d031fbb710000000000000000` (SFC32 seed=42 first 8 bytes
+ FixedClock(0n,0n) REALTIME u64 LE).

Verified against a node WASI shim using the in-tree provider
algorithms; bytes match.
@taybenlor

Copy link
Copy Markdown
Owner

LGTM

@taybenlor taybenlor merged commit bf9dec7 into taybenlor:main Apr 29, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants