merge PR #5671 via eval/pr-5671: fix(bash): drained piped output before timeout return

This commit is contained in:
can1357
2026-07-17 04:37:11 +02:00
6 changed files with 85 additions and 5 deletions
@@ -70,6 +70,9 @@ const shellSessionsInUse = new Set<string>();
*/
const retainedShells = new Set<Shell>();
const RETAIN_REAP_INTERVAL_MS = 5_000;
// Native cancellation may spend two seconds unwinding the shell before its
// N-API chunk bridge drains. The JS watchdog must not race that teardown.
const NATIVE_TIMEOUT_FALLBACK_GRACE_MS = 5_000;
async function retainShellWithLiveBackgroundJobs(shell: Shell): Promise<void> {
let live: number;
@@ -306,16 +309,18 @@ export async function executeBash(command: string, options?: BashExecutorOptions
const nativeTimeoutMs = requestedTimeoutMs !== undefined && requestedTimeoutMs > 0 ? requestedTimeoutMs : undefined;
const nativeOwnsTimeout = nativeTimeoutMs !== undefined;
if (deadlineTimeoutMs !== undefined) {
const fallbackTimeoutMs = nativeOwnsTimeout
? deadlineTimeoutMs + NATIVE_TIMEOUT_FALLBACK_GRACE_MS
: deadlineTimeoutMs;
timeoutTimer = setTimeout(() => {
// Explicit timeouts are already enforced inside pi-natives via
// `timeoutMs`. Do not also abort the JS AbortSignal here: on Windows,
// aborting that signal while a piped command is still forwarding output
// can terminate the Bun host before the native timeout result resolves.
// Explicit timeouts are enforced inside pi-natives via `timeoutMs`.
// Give native cancellation time to flush pipeline output and drain the
// N-API bridge before this result-only watchdog quarantines the run.
if (!nativeOwnsTimeout) {
abortCurrentExecution();
}
timeoutDeferred.resolve("timeout");
}, deadlineTimeoutMs);
}, fallbackTimeoutMs);
}
let resetSession = false;