Skip to content

fork: grow continuations dynamically and recover allocation failures#1043

Open
brandonpayton wants to merge 2 commits into
mainfrom
emdash/fork-instrument-scalable-continuation-buffer-8zssq
Open

fork: grow continuations dynamically and recover allocation failures#1043
brandonpayton wants to merge 2 commits into
mainfrom
emdash/fork-instrument-scalable-continuation-buffer-8zssq

Conversation

@brandonpayton

Copy link
Copy Markdown
Member

Why

Kandelo currently stores an instrumented WebAssembly call stack in a fixed 60 KiB continuation buffer while fork() unwinds. Real fork-heavy programs can exceed that limit, and the resulting write can corrupt adjacent host control memory. Tuning a larger fixed buffer per package only moves the failure point and makes correctness depend on the software being run.

An earlier proposal, PR #862, selected a fixed 128 KiB layout for opted-in binaries. It was closed because it was not truly dynamic and still left a hard ceiling. This PR makes continuation capacity follow the actual live call stack instead.

Allocation failure must also obey the POSIX contract. If Kandelo runs out of continuation memory, or the kernel rejects SYS_FORK, the parent must receive fork() == -1 with errno; the failure must not kill the parent or create a child.

What changed

  • Replace the contiguous continuation payload with host-owned, page-rounded linked chunks.
  • Reserve each exact frame before its first write and publish it only after a complete commit.
  • Allow one continuation frame to span multiple 64 KiB WebAssembly pages.
  • Store only a root anchor in the former fixed save-buffer region; the 60 KiB layout is no longer a continuation capacity limit.
  • Add a strict linked-frame format descriptor so stale or partially instrumented artifacts fail loudly.
  • Add ABORT_UNWINDING, a fourth instrumented state that reconstructs committed inner frames after a later allocation fails.
  • Return root allocation failures directly and replay negative SYS_FORK results through the complete parent continuation.
  • Coordinate the same ownership and cleanup rules across main-process, pthread, nested-child, and the supported direct dynamic-side-module fork path.
  • Keep malformed metadata, impossible transitions, and cleanup failures fatal as integrity errors.
  • Add a P-10 deep-recursion fixture and a V8 test that injects ENOMEM after an inner frame commits, verifies preserved locals and cleanup, then proves a negative complete fork and a later successful retry.

ABI and artifacts

This completes the unpublished ABI 42 fork contract, so ABI_VERSION remains 42. ABI 42 requires:

  • the linked-frame descriptor with transactional-node and abort-replay flags;
  • the three reserve/commit/next imports; and
  • seven wpk_fork_* control exports, including wpk_fork_abort_begin and wpk_fork_abort_end.

ABI 41 and intermediate ABI 42 programs must be rebuilt with the instrumenter in this PR. Package and VFS binary publication is a rollout step and is not included in this source commit.

Instrumented size

These rows use the identical 27,608-byte raw P-10 Wasm fixture and the same dev-shell compiler invocation. P-10 is a deliberately deep conservative-closure stress fixture, not a package-wide estimate.

Instrumenter Instrumented bytes Change
Linked chunks before abort recovery (a4789e2c6) 52,052 baseline
Linked chunks with ABORT_UNWINDING 58,370 +6,318 (+12.14%)

Runtime performance was not measured, so this PR makes no latency or throughput claim.

Validation

Passed:

  • scripts/dev-shell.sh bash scripts/ci-run-test-suite.sh fork-instrument
  • Focused host/V8 suite covering continuation ownership, actual instrumented abort replay, pthread fork, dynamic linking, side-module replay, binary parsing, and fork coverage: 8 files; 128 passed, 2 expected failures, 9 skipped
  • scripts/dev-shell.sh bash -lc 'cd host && npm run build'
  • scripts/dev-shell.sh bash scripts/check-abi-version.sh
  • scripts/dev-shell.sh bash scripts/build-programs.sh
  • ABI snapshot, C header, and generated TypeScript bindings are in sync

Attempted but not fully runnable in this worktree:

  • The browser CI-shaped suite stopped during asset preflight because 35 external package artifacts were absent locally. The runtime implementation is in the shared worker path used by Node.js and browsers, but this PR does not claim browser execution evidence.
  • The complete host suite reached 150 passing files and 13 failing files. Remaining failures were missing wasm64/sysroot fixtures, stale or absent package binaries that predate the seven-export ABI 42 contract, and one artifact-guard timeout. The focused suite passes against programs rebuilt with the current instrumenter.
  • POSIX/libc/Sortix conformance suites and runtime performance benchmarks were not run; this change does not modify the kernel or libc syscall implementation.

Replace the fixed fork continuation capacity with ABI 42 linked, page-backed frame chunks shared across main-process, pthread, browser, Node, and dynamic-linker paths. Preserve truthful fatal cleanup on allocation failure for now and document recoverable ABORT_UNWINDING as the next POSIX-correct step.
Add ABI 42 ABORT_UNWINDING replay so a failed linked-frame allocation reconstructs committed inner frames, releases continuation ownership, and returns the original errno without terminating the process. Negative SYS_FORK results now use the complete parent rewind path as well. Main, pthread, and supported dynamic-side-module paths share the recovery contract; integrity and cleanup failures remain fatal.\n\nThe P-10 stress fixture grows from 52,052 to 58,370 instrumented bytes (+6,318, +12.14%); runtime performance was not measured. The instrumenter suite, focused host/V8 suite, host build, and ABI consistency check pass. Browser validation was attempted but blocked by 35 missing package assets; the broader host suite also remains blocked by missing wasm64/sysroot and stale package artifacts.
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.

1 participant