Recover desktop startup from stale daemon locks
Refs #1950 Refs #1904 Refs #769
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
||||
acquirePidLock,
|
||||
PidLockError,
|
||||
releasePidLock,
|
||||
startPidLockHeartbeat,
|
||||
updatePidLock,
|
||||
} from "../src/server/pid-lock.js";
|
||||
import { resolvePaseoHome } from "../src/server/paseo-home.js";
|
||||
@@ -17,11 +18,13 @@ process.title = "Paseo Supervisor";
|
||||
|
||||
interface DaemonRunnerConfig {
|
||||
devMode: boolean;
|
||||
reclaimStalePidLock: boolean;
|
||||
workerArgs: string[];
|
||||
}
|
||||
|
||||
function parseConfig(argv: string[]): DaemonRunnerConfig {
|
||||
let devMode = false;
|
||||
let reclaimStalePidLock = false;
|
||||
const workerArgs: string[] = [];
|
||||
|
||||
for (const arg of argv) {
|
||||
@@ -29,10 +32,14 @@ function parseConfig(argv: string[]): DaemonRunnerConfig {
|
||||
devMode = true;
|
||||
continue;
|
||||
}
|
||||
if (arg === "--reclaim-stale-pid-lock") {
|
||||
reclaimStalePidLock = true;
|
||||
continue;
|
||||
}
|
||||
workerArgs.push(arg);
|
||||
}
|
||||
|
||||
return { devMode, workerArgs };
|
||||
return { devMode, reclaimStalePidLock, workerArgs };
|
||||
}
|
||||
|
||||
function resolveWorkerEntry(): string {
|
||||
@@ -109,6 +116,7 @@ async function main(): Promise<void> {
|
||||
try {
|
||||
await acquirePidLock(paseoHome, null, {
|
||||
ownerPid: process.pid,
|
||||
reclaimStaleDesktopLock: config.reclaimStalePidLock,
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof PidLockError) {
|
||||
@@ -120,17 +128,29 @@ async function main(): Promise<void> {
|
||||
}
|
||||
|
||||
let lockReleased = false;
|
||||
let requestSupervisorShutdown: ((reason: string) => void) | null = null;
|
||||
const stopLockHeartbeat = startPidLockHeartbeat(paseoHome, {
|
||||
ownerPid: process.pid,
|
||||
onError: (error) => {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
process.stderr.write(`PID lock heartbeat failed: ${message}\n`);
|
||||
if (error instanceof PidLockError) {
|
||||
requestSupervisorShutdown?.("pid_lock_ownership_lost");
|
||||
}
|
||||
},
|
||||
});
|
||||
const releaseLock = async (): Promise<void> => {
|
||||
if (lockReleased) {
|
||||
return;
|
||||
}
|
||||
lockReleased = true;
|
||||
stopLockHeartbeat();
|
||||
await releasePidLock(paseoHome, {
|
||||
ownerPid: process.pid,
|
||||
});
|
||||
};
|
||||
|
||||
runSupervisor({
|
||||
const supervisor = runSupervisor({
|
||||
name: "DaemonRunner",
|
||||
startupMessage: "Starting daemon worker (IPC restart and crash restart enabled)",
|
||||
resolveWorkerEntry: () => workerEntry,
|
||||
@@ -159,6 +179,7 @@ async function main(): Promise<void> {
|
||||
},
|
||||
onSupervisorExit: releaseLock,
|
||||
});
|
||||
requestSupervisorShutdown = supervisor.requestShutdown;
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
|
||||
@@ -47,6 +47,10 @@ interface SupervisorOptions {
|
||||
logFile?: SupervisorLogFileOptions;
|
||||
}
|
||||
|
||||
export interface SupervisorController {
|
||||
requestShutdown(reason: string): void;
|
||||
}
|
||||
|
||||
function describeExit(code: number | null, signal: NodeJS.Signals | null): string {
|
||||
return signal ?? (typeof code === "number" ? `code ${code}` : "unknown");
|
||||
}
|
||||
@@ -105,7 +109,7 @@ function createSupervisorLogStream(options: SupervisorLogFileOptions | undefined
|
||||
});
|
||||
}
|
||||
|
||||
export function runSupervisor(options: SupervisorOptions): void {
|
||||
export function runSupervisor(options: SupervisorOptions): SupervisorController {
|
||||
const restartOnCrash = options.restartOnCrash ?? false;
|
||||
const workerArgs = options.workerArgs ?? process.argv.slice(2);
|
||||
const workerEnv = options.workerEnv ?? process.env;
|
||||
@@ -331,4 +335,6 @@ export function runSupervisor(options: SupervisorOptions): void {
|
||||
process.stdout.write(`[${options.name}] ${options.startupMessage}\n`);
|
||||
writeLifecycleLog(options.startupMessage);
|
||||
spawnWorker();
|
||||
|
||||
return { requestShutdown };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user