


























@@ -9,6 +9,7 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
99import { createLowDiskSpaceWarning } from "../infra/disk-space.js";
1010import { resolveHomeRelativePath } from "../infra/home-dir.js";
1111import { createNpmProjectInstallEnv } from "../infra/npm-install-env.js";
12+import { getProcessStartTime } from "../shared/pid-alive.js";
1213import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
1314import { sanitizeTerminalText } from "../terminal/safe-text.js";
1415import { beginBundledRuntimeDepsInstall } from "./bundled-runtime-deps-activity.js";
@@ -456,65 +457,11 @@ function isProcessAlive(pid: number): boolean {
456457}
457458}
458459459-// Approximate epoch-ms when the current process started, captured once per
460-// Node lifetime so the lock owner record carries a value that is stable
461-// across `Date.now()` clock skew within the running process.
462-const CURRENT_PROCESS_START_TIME_MS = Math.round(Date.now() - process.uptime() * 1000);
463-464-// Approximate epoch-ms when `pid` started running, or null when the platform
465-// or runtime cannot determine it. Used to distinguish two different process
466-// incarnations that happen to share the same numeric PID — most notably
467-// inside Docker containers, where the gateway is always PID 1 (or PID 7
468-// with `init: true`) in its container PID namespace, so a stale lock left
469-// by a previous incarnation looks live to the new one if we only consult
470-// `isAlive(pid)`.
471-function readProcessStartTimeMs(pid: number): number | null {
472-if (!Number.isInteger(pid) || pid <= 0) {
473-return null;
474-}
475-if (process.platform !== "linux") {
476-return null;
477-}
478-let stat: string;
479-try {
480-stat = fs.readFileSync(`/proc/${pid}/stat`, "utf8");
481-} catch {
482-return null;
483-}
484-// /proc/<pid>/stat: PID (comm) state ppid pgrp ... starttime ... where
485-// `comm` may contain spaces and parens, so we anchor on the LAST `)` and
486-// count whitespace-separated fields after it. Field index 22 (1-based) is
487-// starttime, expressed in clock ticks since boot. After the `)` the next
488-// field is `state` (1), then ppid (2), pgrp (3), session (4), tty_nr (5),
489-// tpgid (6), flags (7), minflt (8), cminflt (9), majflt (10), cmajflt
490-// (11), utime (12), stime (13), cutime (14), cstime (15), priority (16),
491-// nice (17), num_threads (18), itrealvalue (19), starttime (20).
492-const lastParen = stat.lastIndexOf(")");
493-if (lastParen === -1) {
494-return null;
495-}
496-const tail = stat
497-.slice(lastParen + 1)
498-.trim()
499-.split(/\s+/);
500-// tail[0] is `state`; starttime is therefore tail[19].
501-const starttimeJiffies = Number(tail[19]);
502-if (!Number.isFinite(starttimeJiffies)) {
503-return null;
504-}
505-// sysconf(_SC_CLK_TCK) is universally 100 on Linux x86/arm targets. We
506-// hard-code that here rather than spawning getconf — the exact tick rate
507-// does not matter for our purposes as long as it is stable: the starttime
508-// we capture for our own process and the starttime we read for the
509-// lock-owner PID are converted with the same constant, so equality holds.
510-const clockTicksPerSecond = 100;
511-const bootTimeMs = Date.now() - os.uptime() * 1000;
512-return Math.round(bootTimeMs + (starttimeJiffies * 1000) / clockTicksPerSecond);
513-}
460+const CURRENT_PROCESS_STARTTIME = getProcessStartTime(process.pid);
514461515462type RuntimeDepsLockOwner = {
516463pid?: number;
517-pidStartTimeMs?: number;
464+starttime?: number;
518465createdAtMs?: number;
519466ownerFileState: "ok" | "missing" | "invalid";
520467ownerFilePath: string;
@@ -558,7 +505,7 @@ function readRuntimeDepsLockOwner(lockDir: string): RuntimeDepsLockOwner {
558505}
559506return {
560507pid: typeof owner?.pid === "number" ? owner.pid : undefined,
561-pidStartTimeMs: typeof owner?.pidStartTimeMs === "number" ? owner.pidStartTimeMs : undefined,
508+starttime: typeof owner?.starttime === "number" ? owner.starttime : undefined,
562509createdAtMs: typeof owner?.createdAtMs === "number" ? owner.createdAtMs : undefined,
563510 ownerFileState,
564511 ownerFilePath,
@@ -584,26 +531,26 @@ function latestFiniteMs(values: readonly (number | undefined)[]): number | undef
584531function shouldRemoveRuntimeDepsLock(
585532owner: Pick<
586533RuntimeDepsLockOwner,
587-"pid" | "pidStartTimeMs" | "createdAtMs" | "lockDirMtimeMs" | "ownerFileMtimeMs"
534+"pid" | "starttime" | "createdAtMs" | "lockDirMtimeMs" | "ownerFileMtimeMs"
588535>,
589536nowMs: number,
590537isAlive: (pid: number) => boolean = isProcessAlive,
591-readStartTimeMs: (pid: number) => number | null = readProcessStartTimeMs,
538+readStarttime: (pid: number) => number | null = getProcessStartTime,
592539): boolean {
593540if (typeof owner.pid === "number") {
594541if (!isAlive(owner.pid)) {
595542return true;
596543}
597-// PID is alive — but inside Docker the new process can share the same
544+// PID is alive, but inside Docker the new process can share the same
598545// PID as the dead writer. If we recorded the writer's start-time and we
599546// can read the live PID's start-time, mismatch means a different
600547// incarnation owns this PID now and the lock is stale. When start-time
601548// evidence is unavailable on either side, fall through to the existing
602549// PID-alive-means-fresh behavior so legacy locks keep working as
603550// before.
604-if (typeof owner.pidStartTimeMs === "number") {
605-const liveStartTimeMs = readStartTimeMs(owner.pid);
606-if (liveStartTimeMs !== null && liveStartTimeMs !== owner.pidStartTimeMs) {
551+if (typeof owner.starttime === "number") {
552+const liveStarttime = readStarttime(owner.pid);
553+if (liveStarttime !== null && liveStarttime !== owner.starttime) {
607554return true;
608555}
609556}
@@ -695,7 +642,9 @@ export function withBundledRuntimeDepsFilesystemLock<T>(
695642`${JSON.stringify(
696643 {
697644 pid: process.pid,
698- pidStartTimeMs: CURRENT_PROCESS_START_TIME_MS,
645+ ...(typeof CURRENT_PROCESS_STARTTIME === "number"
646+ ? { starttime: CURRENT_PROCESS_STARTTIME }
647+ : {}),
699648 createdAtMs: Date.now(),
700649 },
701650 null,
@@ -2476,7 +2425,9 @@ async function withBundledRuntimeDepsInstallRootLockAsync<T>(
24762425`${JSON.stringify(
24772426 {
24782427 pid: process.pid,
2479- pidStartTimeMs: CURRENT_PROCESS_START_TIME_MS,
2428+ ...(typeof CURRENT_PROCESS_STARTTIME === "number"
2429+ ? { starttime: CURRENT_PROCESS_STARTTIME }
2430+ : {}),
24802431 createdAtMs: Date.now(),
24812432 },
24822433 null,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。