Fix flaky passthrough PTY test: spawn with posix_spawn instead of forkpty#35
Merged
Conversation
forkpty() returns into the caller in both the parent and the child, so the child ran Dart code before exec'ing. In a forked child of the multi-threaded Dart VM only the forking thread survives; if a GC or safepoint was in progress at fork() time, the child deadlocks and never exec's the target, leaving the PTY slave open and hanging the master read() — surfacing as a rare 30s timeout in the pty_test SIGINT case. posix_spawn performs the fork+exec entirely inside libc, so no Dart runs in the child and the deadlock class is gone. The child still gets the PTY as its controlling terminal via POSIX_SPAWN_SETSID plus opening the slave without O_NOCTTY. The parent opens the slave once before spawning to absorb the first-open winsize reset so the initial size sticks. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
xvrh
force-pushed
the
claude/relaxed-lalande-03b36d
branch
from
May 16, 2026 06:05
43c2ad8 to
ad27639
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The CI-flaky
pty_test.darttestSIGINT death gives exit 130(30sTimeoutException) was a symptom of a real bug in the passthrough PTY implementation, not a test problem.spawnPtyusedforkpty(), which returns into the caller in both the parent and the child. The child branch then ran Dart code beforeexecing. In a forked child of the multi-threaded Dart VM, only the forking thread survives — if a GC/safepoint was in progress atfork()time, the child deadlocks waiting on VM threads that no longer exist, neverexecs the target, and keeps the PTY slave open, hanging the reader'sread()on the master fd forever.Reproduced deterministically with a stress loop (hung ~1 in 27 spawns);
sampleconfirmed the stuck "child" was still adartvmprocess that never becamebash.Fix
Replaced
forkptywithposix_spawn, which does the fork+exec entirely inside libc — no Dart code runs in the child, eliminating the deadlock class.libc_bindings.dart— swapped theforkpty/execvp/chdir/_exitbindings forposix_openpt,grantpt,unlockpt,ptsname,open, and theposix_spawnfamily.pty_impl.dart—spawn()opens a PTY master and spawns viaposix_spawnpwithPOSIX_SPAWN_SETSID+ file actions (open slave → dup onto stdio, close master). Aposix_spawnfailure maps to exit 127, preserving the old execvp-failure contract.Two subtleties handled:
SETSID+ the child opening the slave withoutO_NOCTTYmakes the PTY its controlling terminal (verified by the SIGWINCHresize()test).0×0, so the parent opens the slave once itself to absorb the reset before setting the size.Test plan
pty_test.dartpass (previously flaked ~2/6)test/passthrough/tests passflutter analyzeclean forlib/src/passthrough/andtest/passthrough/dart tool/prepare_submit.dartformatter appliedposix_spawnpath is portable but only exercised locally on macOS🤖 Generated with Claude Code