fix(subprocess): clean managed processes on host exit

This commit is contained in:
pku-xht
2026-08-11 15:59:43 +08:00
parent b6cd817aca
commit ebe932e24c
18 changed files with 728 additions and 38 deletions

View File

@@ -582,6 +582,25 @@ describe('stdio dispositions', () => {
})
describe('windows tree semantics (injected platform)', () => {
it('host-exit termination routes through taskkill immediately', async () => {
const killed: number[] = []
const running = spawnSubprocess(spec('sleep 60', { graceMs: 60_000 }), {
spillDir,
platform: 'win32',
taskkill: (pid) => {
killed.push(pid)
try {
process.kill(pid, 'SIGKILL')
} catch {
// Already gone — matches taskkill's tolerated not-found status.
}
},
})
running.terminateForHostExit()
await running.done
expect(killed).toEqual([running.pid])
})
it('terminate routes through taskkill by root pid', async () => {
const killed: number[] = []
const running = spawnSubprocess(spec('sleep 60', { graceMs: 100 }), {
@@ -631,6 +650,23 @@ describe('waitForExit', () => {
})
})
describe('synchronous host-exit termination', () => {
it('force-kills the current process tree without waiting for the normal grace', async () => {
const running = spawnSubprocess(spec('trap "" TERM; sleep 60', { graceMs: 60_000 }))
running.terminateForHostExit()
await expect(running.done).resolves.toMatchObject({ exitCode: null, signal: 'SIGKILL' })
await expect(running.waitForExit()).resolves.toBe(true)
const kill = vi.spyOn(process, 'kill')
try {
running.terminateForHostExit()
expect(kill).not.toHaveBeenCalled()
} finally {
kill.mockRestore()
}
})
})
describe('tree-survivor escalation (terminate and bounded waits reach helpers the leader left behind)', () => {
it('terminate() SIGKILLs a TERM-trapping descendant after the direct child settles', async () => {
// The leader spawns a TERM-trapping helper with all stdio detached from