fix(bash): kept persistent shells after cancel
Stopped marking persistent bash sessions as permanently broken when the JavaScript abort or timeout race wins. Stopped the Rust descendant kill-wave helper once no cancellation targets remain so later commands are not swept into old cancels. Fixes #1347
This commit is contained in:
@@ -972,21 +972,22 @@ async fn read_output_bytes(
|
||||
}
|
||||
|
||||
// Rescan-and-signal loop for cancellation. Each pass picks up descendants
|
||||
// spawned during the previous wave's grace period; empty scans keep going so a
|
||||
// cancellation that wins before `run_string` exposes its child still escalates.
|
||||
// spawned during the previous wave's grace period, then exits as soon as no
|
||||
// targets remain so unrelated later commands are not swept into old cancels.
|
||||
async fn terminate_new_descendants<S: std::hash::BuildHasher>(baseline: &HashSet<i32, S>) {
|
||||
const WAVES: u32 = 3;
|
||||
for wave in 0..WAVES {
|
||||
let mut targets = process::TerminationTargets::new();
|
||||
process::add_new_descendants(&mut targets, baseline);
|
||||
if !targets.is_empty() {
|
||||
let signal = if wave == 0 {
|
||||
process::TERM_SIGNAL
|
||||
} else {
|
||||
process::KILL_SIGNAL
|
||||
};
|
||||
targets.signal(signal);
|
||||
if targets.is_empty() {
|
||||
return;
|
||||
}
|
||||
let signal = if wave == 0 {
|
||||
process::TERM_SIGNAL
|
||||
} else {
|
||||
process::KILL_SIGNAL
|
||||
};
|
||||
targets.signal(signal);
|
||||
if wave + 1 < WAVES {
|
||||
let pause = if wave == 0 {
|
||||
Duration::from_millis(75)
|
||||
|
||||
@@ -207,9 +207,6 @@ export async function executeBash(command: string, options?: BashExecutorOptions
|
||||
void runPromise.catch(() => undefined);
|
||||
if (shellSession) {
|
||||
resetSession = true;
|
||||
// Fall back to one-shot execution for the rest of the process once
|
||||
// a persistent session has stopped responding to cancellation.
|
||||
brokenShellSessions.add(sessionKey);
|
||||
}
|
||||
return {
|
||||
exitCode: undefined,
|
||||
|
||||
@@ -197,6 +197,20 @@ describe("executeBash", () => {
|
||||
expect(raced.result.output).toContain("Command cancelled");
|
||||
}
|
||||
expect(abortSpy).toHaveBeenCalled();
|
||||
|
||||
vi.restoreAllMocks();
|
||||
|
||||
await executeBash("export PI_AFTER_ABORT=still_persistent", {
|
||||
cwd: tempDir,
|
||||
timeout: 5000,
|
||||
sessionKey: "hung-native-abort",
|
||||
});
|
||||
const next = await executeBash("printf '%s\n' \"$PI_AFTER_ABORT\"", {
|
||||
cwd: tempDir,
|
||||
timeout: 5000,
|
||||
sessionKey: "hung-native-abort",
|
||||
});
|
||||
expect(next.output.trim()).toBe("still_persistent");
|
||||
});
|
||||
|
||||
it("returns at the JavaScript timeout when native timeout cleanup stalls", async () => {
|
||||
|
||||
Reference in New Issue
Block a user