mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 12:37:40 +00:00
fix(process): harden graceful kill-tree cancellation semantics
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { killProcessTree } from "../../kill-tree.js";
|
||||
import type { ManagedRunStdin } from "../types.js";
|
||||
import { killProcessTree } from "../../kill-tree.js";
|
||||
import { toStringEnv } from "./env.js";
|
||||
|
||||
const FORCE_KILL_WAIT_FALLBACK_MS = 4000;
|
||||
|
||||
type PtyExitEvent = { exitCode: number; signal?: number };
|
||||
type PtyDisposable = { dispose: () => void };
|
||||
type PtySpawnHandle = {
|
||||
@@ -70,11 +72,21 @@ export async function createPtyAdapter(params: {
|
||||
| null = null;
|
||||
let waitPromise: Promise<{ code: number | null; signal: NodeJS.Signals | number | null }> | null =
|
||||
null;
|
||||
let forceKillWaitFallbackTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
const clearForceKillWaitFallback = () => {
|
||||
if (!forceKillWaitFallbackTimer) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(forceKillWaitFallbackTimer);
|
||||
forceKillWaitFallbackTimer = null;
|
||||
};
|
||||
|
||||
const settleWait = (value: { code: number | null; signal: NodeJS.Signals | number | null }) => {
|
||||
if (waitResult) {
|
||||
return;
|
||||
}
|
||||
clearForceKillWaitFallback();
|
||||
waitResult = value;
|
||||
if (resolveWait) {
|
||||
const resolve = resolveWait;
|
||||
@@ -83,6 +95,16 @@ export async function createPtyAdapter(params: {
|
||||
}
|
||||
};
|
||||
|
||||
const scheduleForceKillWaitFallback = (signal: NodeJS.Signals) => {
|
||||
clearForceKillWaitFallback();
|
||||
// Some PTY hosts fail to emit onExit after kill; use a delayed fallback
|
||||
// so callers can still unblock without marking termination immediately.
|
||||
forceKillWaitFallbackTimer = setTimeout(() => {
|
||||
settleWait({ code: null, signal });
|
||||
}, FORCE_KILL_WAIT_FALLBACK_MS);
|
||||
forceKillWaitFallbackTimer.unref();
|
||||
};
|
||||
|
||||
exitListener =
|
||||
pty.onExit((event) => {
|
||||
const signal = event.signal && event.signal !== 0 ? event.signal : null;
|
||||
@@ -151,9 +173,10 @@ export async function createPtyAdapter(params: {
|
||||
} catch {
|
||||
// ignore kill errors
|
||||
}
|
||||
// Some PTY hosts do not emit `onExit` reliably after kill.
|
||||
// Ensure waiters can progress on forced termination.
|
||||
settleWait({ code: null, signal });
|
||||
|
||||
if (signal === "SIGKILL") {
|
||||
scheduleForceKillWaitFallback(signal);
|
||||
}
|
||||
};
|
||||
|
||||
const dispose = () => {
|
||||
@@ -167,6 +190,7 @@ export async function createPtyAdapter(params: {
|
||||
} catch {
|
||||
// ignore disposal errors
|
||||
}
|
||||
clearForceKillWaitFallback();
|
||||
dataListener = null;
|
||||
exitListener = null;
|
||||
settleWait({ code: null, signal: null });
|
||||
|
||||
Reference in New Issue
Block a user