diff --git a/scripts/measure-rpc-rtt.mjs b/scripts/measure-rpc-rtt.mjs index a3a8f70143e4..88f1f52d2752 100644 --- a/scripts/measure-rpc-rtt.mjs +++ b/scripts/measure-rpc-rtt.mjs @@ -309,6 +309,12 @@ export function signalGatewayProcess( if (!result?.error && result?.status === 0) { return true; } + if (signal !== "SIGKILL") { + const forceResult = runTaskkill("taskkill", [...args, "/F"], { stdio: "ignore" }); + if (!forceResult?.error && forceResult?.status === 0) { + return true; + } + } } try { return child.kill(signal); diff --git a/test/scripts/measure-rpc-rtt.test.ts b/test/scripts/measure-rpc-rtt.test.ts index 71bb5a0cd16d..56223ee37f63 100644 --- a/test/scripts/measure-rpc-rtt.test.ts +++ b/test/scripts/measure-rpc-rtt.test.ts @@ -367,6 +367,36 @@ describe("scripts/measure-rpc-rtt.mjs", () => { expect(child.kill).not.toHaveBeenCalled(); }); + it("force-kills Windows gateway process trees when graceful taskkill fails", () => { + const child = Object.assign(new EventEmitter(), { + exitCode: null, + kill: vi.fn(), + pid: 12345, + signalCode: null, + }); + const kill = vi.fn(() => true); + const runTaskkill = vi + .fn() + .mockReturnValueOnce({ error: undefined, status: 1 }) + .mockReturnValueOnce({ error: undefined, status: 0 }); + + expect( + signalGatewayProcess(child, "SIGTERM", kill, { + platform: "win32", + runTaskkill, + }), + ).toBe(true); + + expect(runTaskkill).toHaveBeenNthCalledWith(1, "taskkill", ["/PID", "12345", "/T"], { + stdio: "ignore", + }); + expect(runTaskkill).toHaveBeenNthCalledWith(2, "taskkill", ["/PID", "12345", "/T", "/F"], { + stdio: "ignore", + }); + expect(kill).not.toHaveBeenCalled(); + expect(child.kill).not.toHaveBeenCalled(); + }); + it("treats missing gateway process groups as already exited", () => { const child = Object.assign(new EventEmitter(), { exitCode: null,