ReviewOS

home-lang/home

Native POSIX spawn: propagate direct errno and file-action setup failures

#493
Closed chris opened this 8 days ago ·
8 days ago

Status: implemented and verified in 6d797186c on codex/runtime-port; open pending integration into main.

Follow-up to #66 and the fake-clock work in #490.

Root cause and implementation

A native stack sample showed that the unchanged timers-immediate-exception-fixture.js hung during module evaluation, before scheduling its callbacks. Node's common ESM helper eagerly reads common.opensslCli, which calls child_process.spawnSync on an absent absolute executable. Home passed libc posix_spawn's direct positive errno to a -1/errno-style helper, treated ENOENT as success and watched PID 0. A direct missing-absolute-path probe reproduced this; a small handled-immediate ordering probe completed correctly.

Home now preserves posix_spawn's errno and executable path, propagates file-action/attribute setup failures instead of discarding them, and releases the extra-fd registration list on Maybe.err as well as Zig errors. Native object cleanup runs on every return path; successful spawns transfer the list into their owning process. wait4 keeps its separate -1/errno contract. No timer or exception-dispatch implementation was changed.

Verification

HOME_NATIVE_VM=1 HOME_CORPUS_FULL_VM=1 ./zig-out/bin/home-debug run packages/runtime/test/bun-corpus/js/node/timers/timers-immediate-exception-fixture.js
HOME_NATIVE_VM=1 HOME_CORPUS_FULL_VM=1 ./zig-out/bin/home-debug test packages/runtime/test/bun-corpus/js/node/timers/node-timers.test.ts

The unchanged standalone fixture exits successfully, including its real nextTick/microtask order and common.mustCall checks. The full Node timer file passes 20 tests / 31 assertions; before this fix, both the prior Home binary and the fake-clock binary hung after printing 19 passes. Bun's original-checkout control passes 20 / 31 too.

The selected missing-command and Darwin-fd-10240 tests pass 2 tests / 9 assertions (32 filtered cases excluded). The expanded native CLI test repeats absolute missing-path, EACCES and invalid-cwd failures, checks invalid extra-fd setup and descriptor counts, proves valid spawning still works, and verifies that spawnSync does not execute user microtasks. All 21 native regression files pass. Adjacent unchanged checks pass for isolation 19 / 67, core fake timers 30 / 83, timers/promises 4 / 7, and PATH selection 1 / 2. In total, 28 selected commands succeed. Standalone/native commands are not added to upstream test counts.

Native build and cached rebuild: 17/17 steps. Focused Pickier, spawn-wrapper Zig formatting and whitespace checks pass. Eight relevant corpus/helper files were verified byte-for-byte against source pin 4982b91e3702094330f3be3883354c52b8c01323.

Remaining scope

Full child-process: 30 pass / 2 fail / 1 skip / 1 TODO / 70 assertions, with failures tracked as extra stdio #495 and package scripts #497. Full spawnSync: 4 pass / 1 fail / 1 platform skip / 11 assertions, tracked as native counter snapshots #496. Installed Bun 1.3.14 passes those scenarios but fails the newer invalid-fd case itself (full child-process: 31 pass / 1 fail / 1 skip / 1 TODO / 71 assertions). Linux/Windows/PTY coverage, wider native ownership and full-suite parity remain open. No incomplete suite, expected failure, skip or TODO counts as logical completion.

8 days ago

The neighboring fake-clock work is published as d0ed7c906 under #490 and cross-referenced in #66. This issue remains unresolved: current and pre-change Home both time out on the unchanged standalone immediate-exception fixture, while Bun exits successfully. The full Node timer file still hangs after 19 tests; neither changing working directory nor the fake-clock implementation fixes it. Both 5,000-callback nested-immediate tests passed before reaching this fixture, so the observed hang should not be attributed to their workload.

8 days ago

Root cause correction

A native stack sample shows the hang occurs during module evaluation, before the immediate callbacks are scheduled. Node's common ESM helper eagerly reads common.opensslCli; that getter uses child_process.spawnSync on an absent absolute openssl-cli path. Home's POSIX spawn wrapper passed the positive return code from libc posix_spawn to a -1/errno-style helper, treated ENOENT as success and attempted to watch PID 0. A standalone missing absolute executable reproduces the hang. A small handled-immediate ordering probe completes successfully.

The wrapper also discarded native file-action and attribute setup errors. The current pinned Bun implementation preserves both direct return-code errors and action-registration errors, including Darwin EBADF for fd 10240. Porting that error handling and setup propagation now; verification is still pending. No change to callback scheduling is justified by this trace.

The original timer fixture remains the integration regression: it must finish its real callback and common.mustCall checks after the import no longer hangs. Keep the full timer suite, missing-path/permission errors, invalid fd setup and natural process exit as required verification.

8 days ago

Native POSIX spawn checkpoint — 6d797186c

Published on codex/runtime-port as Chris Breuer, without coauthors; not yet merged to main. #493 is now implemented and verified. The timer fixture hung during import, before scheduling immediates: Node's common helper spawned a missing absolute openssl-cli, and Home misread libc's direct positive errno as success and watched PID 0. The fix propagates spawn and file-action/attribute setup errors, preserves executable paths, and releases failed registration lists. No immediate dispatch code was changed.

Verification: 28 selected commands pass, including all 21 native regression files, the unchanged complete Node timer file (20 tests / 31 assertions), its standalone fixture, isolation (19 / 67), core fake clocks (30 / 83), timers/promises (4 / 7), PATH selection (1 / 2) and the selected missing-command/invalid-fd cases (2 / 9; 32 filtered out excluded). Native build and cached rebuild 17/17; repeated native spawn failures preserve descriptor counts and recover to a valid spawn without re-entering user microtasks. Eight relevant upstream files are byte-identical to source pin 4982b91e3702094330f3be3883354c52b8c01323.

New linked follow-ups keep the failing boundaries explicit: extra stdio pipes #495, real native counter snapshots #496, and package-script passthrough #497. Full child-process remains 30 pass / 2 fail / 1 skip / 1 TODO / 70 assertions; spawnSync remains 4 pass / 1 fail / 1 platform skip / 11 assertions. Installed Bun 1.3.14 passes those three failing scenarios but fails the newer invalid-fd case itself. Deferred imports #491, parallel test orchestration #485, full worker timing #482 and the other open work remain. The full port and 100% logical Bun-suite compatibility are still incomplete.

8 days ago

Follow-up integrated on main in 8c56f61c6: the POSIX spawn adapter now carries new_process_group into native POSIX_SPAWN_SETPGROUP flags. The option was present in Home's Attr but previously discarded by the Darwin system-spawn conversion.

The unchanged #485 coordinator SIGTERM case is the integration proof: two real Home workers each spawn a grandchild, the coordinator receives SIGTERM, and all four descendants disappear within the original deadline. Before the mapping, all four survived even after the coordinator reacted to the signal. The complete unchanged parallel file now passes 27/27 tests. This is Darwin execution evidence; it does not establish Linux/Windows/PTY coverage or complete the broader #66 ledger.

8 days ago

Closing as integrated on main. The implementation commit 6d79718 is an ancestor of current main. Fresh Darwin arm64 ReleaseFast verification passes the unchanged complete Node timers file at 20/20 tests and 31 assertions, the standalone immediate fixture, the complete child-process file at 32 runnable passes / 72 assertions / 0 failures, and the fresh aggregate build. Direct errno and file-action setup failures remain covered by the native regression committed with the implementation. Linux and Windows execution is now tracked explicitly in #678; whole-suite parity remains #66.

Sign in to comment on this issue.