fix: stop orphaned macOS cores on restart

Closes #2115
This commit is contained in:
zjdndjf
2026-08-30 17:55:51 +08:00
parent 26dd08e07b
commit d50e281383
2 changed files with 26 additions and 3 deletions

View File

@@ -82,6 +82,7 @@ const execFilePromise = promisify(execFile)
const ctlParam = process.platform === 'win32' ? '-ext-ctl-pipe' : '-ext-ctl-unix'
const coreHookTimeout = 30000
const automaticRestartDelay = 750
const coreShutdownTimeout = 500
const coreProcessNames = ['mihomo', 'mihomo-alpha', 'mihomo-smart'] as const
// 核心进程状态
@@ -330,10 +331,10 @@ async function stopPidFileCore(): Promise<void> {
try {
if (await verifyProcessOwner(pid, coreProcessNames)) {
process.kill(pid, 'SIGINT')
const deadline = Date.now() + 500
const deadline = Date.now() + coreShutdownTimeout
let stillRunning = true
while (stillRunning && Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 50))
await delay(50)
try {
process.kill(pid, 0)
} catch {
@@ -856,9 +857,30 @@ export async function stopCoreForExit(): Promise<void> {
setStopCoreBeforeAdminRestart(stopCore)
async function ensureCoreProcessExited(proc: ChildProcess | null): Promise<void> {
if (!proc) return
const waitForExit = async (): Promise<boolean> => {
const deadline = Date.now() + coreShutdownTimeout
while (proc.exitCode === null && proc.signalCode === null && Date.now() < deadline) {
await delay(50)
}
return proc.exitCode !== null || proc.signalCode !== null
}
if (await waitForExit()) return
managerLogger.warn(`Core PID ${proc.pid ?? 'unknown'} did not exit after SIGINT; sending SIGKILL`)
proc.kill('SIGKILL')
if (!(await waitForExit())) {
throw new Error(`Core PID ${proc.pid ?? 'unknown'} is still running after SIGKILL`)
}
}
async function restartCoreOnce(forceStop: boolean): Promise<void> {
const startAttempt = await runCoreOperation(async () => {
const previousChild = child
await stopCoreInternal(forceStop)
if (process.platform === 'darwin') await ensureCoreProcessExited(previousChild)
return startCoreInternal(false, true)
})
await startAttempt.readiness

View File

@@ -199,7 +199,8 @@ export async function verifyProcessOwner(
if (!match || parseInt(match[2], 10) !== pid) return false
processName = match[1]
} else {
const { stdout } = await execFilePromise('ps', ['-p', `${pid}`, '-o', 'comm='], {
const nameField = process.platform === 'darwin' ? 'ucomm=' : 'comm='
const { stdout } = await execFilePromise('ps', ['-p', `${pid}`, '-o', nameField], {
timeout: 1000
})
processName = stdout.trim().split(/\r?\n/, 1)[0] || ''