🐛 Settle process join() only after stdout/stderr pumps complete - #245
🐛 Settle process join() only after stdout/stderr pumps complete#245taras wants to merge 1 commit into
Conversation
Process.join() could resolve on the child-process close event while the stdout/stderr pump tasks were still forwarding the final chunks through Stdio middleware, so callers could observe an exit status before all output was delivered. - posix: wait for both pump done-resolvers after the close event before resolving processResult, matching the win32 adapter's existing drain-before-result ordering - both adapters: a failing Stdio handler now resolves processResult with Err so join()/expect() throw instead of hanging, and the pumps always resolve their done-resolvers and close their signals on the way out - add an internal CloseEvent context so tests can order assertions deterministically around the close event without scheduler sleeps Fixes #244
📝 WalkthroughWalkthroughProcess execution now invokes registered close callbacks, waits for stdout and stderr pumps before settling, and propagates output-processing errors. Tests cover output completeness and error handling for ChangesProcess completion handling
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error)
✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
commit: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@process/test/exec.test.ts`:
- Around line 388-390: Update the stderr assertion in the joined execution test
around stderrAtSettle to compare the complete normalized fixture output,
matching the stdout test, instead of using toContain("boom\n"). Preserve the
existing status check and verify the full stderr value at settlement.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 5d316826-a798-417b-a4dc-da4a93569f6b
📒 Files selected for processing (4)
process/src/exec/internal.tsprocess/src/exec/posix.tsprocess/src/exec/win32.tsprocess/test/exec.test.ts
| const status = yield* joined; | ||
| expect(status.code).toEqual(0); | ||
| expect(stderrAtSettle).toContain("boom\n"); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Assert the complete stderr value.
toContain("boom\n") passes if a later stderr chunk is missing. Compare stderrAtSettle with the complete normalized fixture output, as the stdout test does. This verifies the stated stderr-completeness contract.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@process/test/exec.test.ts` around lines 388 - 390, Update the stderr
assertion in the joined execution test around stderrAtSettle to compare the
complete normalized fixture output, matching the stdout test, instead of using
toContain("boom\n"). Preserve the existing status check and verify the full
stderr value at settlement.
Replace the fromReadable pump architecture in both adapters with a single createNativeProcess core that wires the child process entirely through native Node listeners attached in the same synchronous continuation as the spawn. posix and win32 reduce to SpawnStrategy objects: how to spawn, and how to shut down. - the orphan window from #236 closes: a guard teardown registers before the child exists and the spawn plus all listener wiring follow with no suspension points in between - close-settled means raw-output-complete by construction (#244): Node emits "close" only after both stdio streams have closed, and chunk delivery into the raw signals is synchronous with stream emission - Stdio middleware runs as consumer tasks over the raw signals; a sequencer settles join()/expect() only after the close event and both consumers draining, and a failing handler resolves the result with Err instead of hanging - the graceful-shutdown drain contract is preserved: the primary teardown registers after the consumers, so they are still alive to forward output produced during termination - the eval scope remains solely to service around(); documented at the point of use Includes the halt-sweep regression test originated in #237 and the output-completeness and middleware-failure tests originated in #245, each verified to fail when its guarantee is mutated out. Fixes #236 Fixes #244
Motivation
Addresses #244. On POSIX,
Process.join()/expect()resolved as soon as the child-processcloseevent fired, while the stdout/stderr pump tasks could still be forwarding the final chunks throughStdiomiddleware. A caller could observe the exit status before all output had passed through its middleware and the public signals. The Windows adapter already waited for the pumps before resolving.Approach
closeevent, the close watcher waits for both pump done-resolvers before resolvingprocessResult— the same drain-before-result ordering win32 already had, so a blockedStdiohandler provides real backpressure againstjoin().Stdiohandler resolvesprocessResultwithErr, sojoin()/expect()throw instead of hanging; pumps always resolve their done-resolvers and close their signals in a synchronousfinally.CloseEventseam (src/exec/internal.ts, not exported frommod.ts) lets the regression tests order assertions deterministically around the close event without scheduler sleeps. Mutation-verified: with the pump wait removed, both completeness tests fail 5/5.