mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 19:01:25 +00:00
fix(sandbox): serialize registry mutations and lock usage
This commit is contained in:
@@ -1,12 +1,18 @@
|
|||||||
import { mkdtempSync } from "node:fs";
|
|
||||||
import fs from "node:fs/promises";
|
import fs from "node:fs/promises";
|
||||||
import { tmpdir } from "node:os";
|
|
||||||
import path from "node:path";
|
|
||||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
const TEST_STATE_DIR = mkdtempSync(path.join(tmpdir(), "openclaw-sandbox-registry-"));
|
const { TEST_STATE_DIR, SANDBOX_REGISTRY_PATH, SANDBOX_BROWSER_REGISTRY_PATH } = vi.hoisted(() => {
|
||||||
const SANDBOX_REGISTRY_PATH = path.join(TEST_STATE_DIR, "containers.json");
|
const path = require("node:path");
|
||||||
const SANDBOX_BROWSER_REGISTRY_PATH = path.join(TEST_STATE_DIR, "browsers.json");
|
const { mkdtempSync } = require("node:fs");
|
||||||
|
const { tmpdir } = require("node:os");
|
||||||
|
const baseDir = mkdtempSync(path.join(tmpdir(), "openclaw-sandbox-registry-"));
|
||||||
|
|
||||||
|
return {
|
||||||
|
TEST_STATE_DIR: baseDir,
|
||||||
|
SANDBOX_REGISTRY_PATH: path.join(baseDir, "containers.json"),
|
||||||
|
SANDBOX_BROWSER_REGISTRY_PATH: path.join(baseDir, "browsers.json"),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
vi.mock("./constants.js", () => ({
|
vi.mock("./constants.js", () => ({
|
||||||
SANDBOX_STATE_DIR: TEST_STATE_DIR,
|
SANDBOX_STATE_DIR: TEST_STATE_DIR,
|
||||||
@@ -183,8 +189,8 @@ describe("registry race safety", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
removeRegistryEntry("container-x"),
|
|
||||||
updateRegistry(containerEntry({ containerName: "container-x", configHash: "updated" })),
|
updateRegistry(containerEntry({ containerName: "container-x", configHash: "updated" })),
|
||||||
|
removeRegistryEntry("container-x"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const registry = await readRegistry();
|
const registry = await readRegistry();
|
||||||
@@ -224,8 +230,8 @@ describe("registry race safety", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
removeBrowserRegistryEntry("browser-x"),
|
|
||||||
updateBrowserRegistry(browserEntry({ containerName: "browser-x", configHash: "updated" })),
|
updateBrowserRegistry(browserEntry({ containerName: "browser-x", configHash: "updated" })),
|
||||||
|
removeBrowserRegistryEntry("browser-x"),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const registry = await readBrowserRegistry();
|
const registry = await readBrowserRegistry();
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ function isRegistryFile<T extends RegistryEntry>(value: unknown): value is Regis
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function withRegistryLock<T>(registryPath: string, fn: () => Promise<T>): Promise<T> {
|
async function withRegistryLock<T>(registryPath: string, fn: () => Promise<T>): Promise<T> {
|
||||||
const lock = await acquireSessionWriteLock({ sessionFile: registryPath });
|
const lock = await acquireSessionWriteLock({ sessionFile: registryPath, allowReentrant: false });
|
||||||
try {
|
try {
|
||||||
return await fn();
|
return await fn();
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -375,6 +375,7 @@ export async function acquireSessionWriteLock(params: {
|
|||||||
timeoutMs?: number;
|
timeoutMs?: number;
|
||||||
staleMs?: number;
|
staleMs?: number;
|
||||||
maxHoldMs?: number;
|
maxHoldMs?: number;
|
||||||
|
allowReentrant?: boolean;
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
release: () => Promise<void>;
|
release: () => Promise<void>;
|
||||||
}> {
|
}> {
|
||||||
@@ -394,8 +395,9 @@ export async function acquireSessionWriteLock(params: {
|
|||||||
const normalizedSessionFile = path.join(normalizedDir, path.basename(sessionFile));
|
const normalizedSessionFile = path.join(normalizedDir, path.basename(sessionFile));
|
||||||
const lockPath = `${normalizedSessionFile}.lock`;
|
const lockPath = `${normalizedSessionFile}.lock`;
|
||||||
|
|
||||||
|
const allowReentrant = params.allowReentrant ?? true;
|
||||||
const held = HELD_LOCKS.get(normalizedSessionFile);
|
const held = HELD_LOCKS.get(normalizedSessionFile);
|
||||||
if (held) {
|
if (allowReentrant && held) {
|
||||||
held.count += 1;
|
held.count += 1;
|
||||||
return {
|
return {
|
||||||
release: async () => {
|
release: async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user