feat: added Windows process handle duplication and signal handling for safe job termination

- Added Windows-specific process handle duplication mechanism to safely manage process termination across multiple handles.
- Implemented Windows signal handling support with Signal enum variants (Terminate, Kill, Interrupt) and kill_process() function using Windows API.
- Added background job termination on cancellation with platform-specific implementations (TERM/KILL signals on Unix, TerminateProcess on Windows).
- Implemented cancellation token support in shell execution with proper cleanup of background jobs when timeout or abort occurs.
- Added test coverage for background job termination on timeout and abort scenarios.
This commit is contained in:
can1357
2026-02-05 08:10:52 +01:00
parent 71f3acb6dc
commit 1a2e833974
5 changed files with 230 additions and 7 deletions
@@ -212,6 +212,46 @@ describe("executeBash", () => {
expect(fs.existsSync(marker)).toBe(false);
});
it("kills background jobs on timeout", async () => {
if (process.platform === "win32") return;
const marker = path.join(tempDir, "marker-bg.txt");
const markerEscaped = marker.replace(/'/g, "'\\''");
const result = await executeBash(`{ sleep 2; echo done > '${markerEscaped}'; } & sleep 10`, {
cwd: tempDir,
timeout: 100,
});
expect(result.cancelled).toBe(true);
await Bun.sleep(3000);
expect(fs.existsSync(marker)).toBe(false);
});
it("kills background jobs on abort", async () => {
if (process.platform === "win32") return;
const marker = path.join(tempDir, "marker-bg-abort.txt");
const markerEscaped = marker.replace(/'/g, "'\\''");
const controller = new AbortController();
const promise = executeBash(`{ sleep 2; echo done > '${markerEscaped}'; } & sleep 10`, {
cwd: tempDir,
timeout: 10000,
signal: controller.signal,
});
await Bun.sleep(100);
controller.abort();
const result = await promise;
expect(result.cancelled).toBe(true);
await Bun.sleep(3000);
expect(fs.existsSync(marker)).toBe(false);
});
it("kills spawned process on abort (not just orphans it)", async () => {
if (process.platform === "win32") return;