Conversation
Performance Benchmark Report
Code Coverage Report: Only Changed Files listed
Minimum allowed coverage is Test Summary: bash-completion test suite
|
0934b05 to
91cc76a
Compare
1beace1 to
fd4e0be
Compare
fd4e0be to
fa9fdb3
Compare
fa9fdb3 to
5eb515f
Compare
|
Rebased onto current The rebase was not mechanical: #1274 moved Result: Validation at
(Linux/x86_64 locally; other platforms via CI.) On #1276: complementary, not overlapping. Same invariant, two different dispatch sites — that PR fixes This is refreshed and ready for review — could it go into the needs-review queue? |
|
The six red All six fail at the
|
A compound command used as a non-final pipeline stage ran inline to completion. Stages are set up in order, so the stage that would drain its output did not exist yet; once the compound command wrote more than one pipe buffer (64 KiB on Linux and macOS) it blocked forever. Spawn it onto a blocking task when it owns its shell, the same shape execute_via_builtin_in_owned_shell already uses for builtin stages. The parent-shell case -- a single-command pipeline, or the last stage under lastpipe -- still runs inline, preserving its side effects. That path already executed against a shell clone, so only *when* the stage runs changes, not which shell it mutates. Since reubeno#1274, [[ ... ]] is CompoundCommand::ExtendedTest and so reaches this arm. That needs no exception: a bare [[ ... ]] is a single-command pipeline and stays on the parent-shell path, so BASH_REMATCH is unaffected, and as a non-final stage it was already evaluating against a discarded clone. Assisted-by: Claude Code:claude-opus-4-8 Assisted-by: Claude Code:claude-opus-5
5eb515f to
c6c8ac1
Compare
|
Out of draft. Since the last pass: merged with current 23 lines across |
The comment said an owned shell means a non-final stage. That is not always true: with lastpipe off (or job control on) the final stage gets an owned shell too. What actually makes spawning safe is that the owned shell is a throwaway clone whose mutations are discarded; say that instead. Comment only, no behavior change. Assisted-by: Claude Code:claude-opus-5
|
I did several passes on this before allowing the agent to flip this out of "draft" it should be fairly concise now. |
The Fedora OS-target job failed on the compat case "Process substitution: input + output" (cp <(echo hi) >(cat)). That case is unrelated to this PR: it is a pre-existing race in setup_process_substitution, which fires the subshell with a bare tokio::spawn and never tracks it, while entry.rs calls std::process::exit as soon as the main future returns. If the outer command finishes before a worker picks up the task, the substitution never runs and its output is lost. Same root cause as issue reubeno#1140, which reproduces deterministically: brush -c 'echo ok 1> >(tr o O)' # prints nothing; bash prints Ok This PR touches only the Compound arm of pipeline execution; the failing case is a simple command and never reaches it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Hi, and thanks for this fix. Have you seen #1359 ? According to my tests, on windows, a pipe hangs if it passes more than 4096 chars, but the latest CI build of this PR (of commit de64416) does not fix it on windows. To me it looks like the same or similar issue as this PR tries to fix, but I don't know that for a fact. Is this PR supposed to fix #1359 too? If yes, could you please try the test script at the issue, and check whether it's fixed on windows or not? For me it's not yet fixed. Thanks. |
|
@hartsock ? (previous comment) |
brush runs a compound command in a non-final pipeline stage inline to completion, so the stage that would drain its output never starts. Once the compound writes past one pipe buffer (64 KiB on Linux/macOS), it blocks forever. bash completes both of these.
The common shape is
... | while read x; do ...; done | sort | head.Fix. Spawn the stage when it owns its shell — the shape
execute_via_builtin_in_owned_shellalready uses for builtins. An owned shell is a throwaway clone whose mutations are discarded, so only when the stage runs changes, not which shell it mutates. Parent-shell stages — a single-command pipeline, or the last stage underlastpipe— still run inline and keep their side effects.Validation.
cargo xtask ci pre-commitclean; compat gains exactly the one added case (2301 ran: 1845 succeeded, 0 failed, 456 known to fail, againstmain's2300/1844). Applied alone to unfixedmainthat case fails: the shell hangs and the harness kills it, so a regression fails the suite rather than hanging CI. Full numbers. Merged with currentmain(812336d).Two things a reviewer may expect to be wrong:
[[ ... ]], and overlap with #1276[[ ... ]]since #1274. It is nowCompoundCommand::ExtendedTest, so it reaches this arm. No exception is warranted: a bare[[ $x =~ $re ]]is a single-command pipeline and stays on the parent-shell path, soBASH_REMATCHis unaffected; as a non-final stage it was already evaluating against a discarded clone. Adding an exception would hand-rebuild the special case #1274 removed.#1276 is complementary — measured, not assumed. Each repro still hangs under the other's fix (
timeout 15, exit 124 = hung):big | wc -lwhile …; done | wc -lmain30a3bceThat PR fixes
execute_via_functionincommands.rs; this one fixes theCompoundarm ininterp.rs. We both append topipeline.yaml, so whoever lands second takes a trivial conflict — happy to be the one who rebases.