mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-23 15:11:42 +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 { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const TEST_STATE_DIR = mkdtempSync(path.join(tmpdir(), "openclaw-sandbox-registry-"));
|
||||
const SANDBOX_REGISTRY_PATH = path.join(TEST_STATE_DIR, "containers.json");
|
||||
const SANDBOX_BROWSER_REGISTRY_PATH = path.join(TEST_STATE_DIR, "browsers.json");
|
||||
const { TEST_STATE_DIR, SANDBOX_REGISTRY_PATH, SANDBOX_BROWSER_REGISTRY_PATH } = vi.hoisted(() => {
|
||||
const path = require("node:path");
|
||||
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", () => ({
|
||||
SANDBOX_STATE_DIR: TEST_STATE_DIR,
|
||||
@@ -183,8 +189,8 @@ describe("registry race safety", () => {
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
removeRegistryEntry("container-x"),
|
||||
updateRegistry(containerEntry({ containerName: "container-x", configHash: "updated" })),
|
||||
removeRegistryEntry("container-x"),
|
||||
]);
|
||||
|
||||
const registry = await readRegistry();
|
||||
@@ -224,8 +230,8 @@ describe("registry race safety", () => {
|
||||
};
|
||||
|
||||
await Promise.all([
|
||||
removeBrowserRegistryEntry("browser-x"),
|
||||
updateBrowserRegistry(browserEntry({ containerName: "browser-x", configHash: "updated" })),
|
||||
removeBrowserRegistryEntry("browser-x"),
|
||||
]);
|
||||
|
||||
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> {
|
||||
const lock = await acquireSessionWriteLock({ sessionFile: registryPath });
|
||||
const lock = await acquireSessionWriteLock({ sessionFile: registryPath, allowReentrant: false });
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
|
||||
@@ -375,6 +375,7 @@ export async function acquireSessionWriteLock(params: {
|
||||
timeoutMs?: number;
|
||||
staleMs?: number;
|
||||
maxHoldMs?: number;
|
||||
allowReentrant?: boolean;
|
||||
}): Promise<{
|
||||
release: () => Promise<void>;
|
||||
}> {
|
||||
@@ -394,8 +395,9 @@ export async function acquireSessionWriteLock(params: {
|
||||
const normalizedSessionFile = path.join(normalizedDir, path.basename(sessionFile));
|
||||
const lockPath = `${normalizedSessionFile}.lock`;
|
||||
|
||||
const allowReentrant = params.allowReentrant ?? true;
|
||||
const held = HELD_LOCKS.get(normalizedSessionFile);
|
||||
if (held) {
|
||||
if (allowReentrant && held) {
|
||||
held.count += 1;
|
||||
return {
|
||||
release: async () => {
|
||||
|
||||
Reference in New Issue
Block a user