mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 03:45:46 -06:00
fix(llama-cpp): fence native runtime handoff
Punchcard-Session: frost-brook-timber-mx
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
import { beforeEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
createLlamaCppInferenceRuntimeToken,
|
||||
LlamaCppInferenceRestartRequiredError,
|
||||
} from "./inference-runtime-coordinator.js";
|
||||
|
||||
const testApi = (globalThis as Record<PropertyKey, unknown>)[
|
||||
Symbol.for("openclaw.llamaCppInferenceTestApi")
|
||||
] as {
|
||||
resetInferenceRuntimeCoordinator: () => void;
|
||||
};
|
||||
|
||||
function createLiveToken() {
|
||||
let live = true;
|
||||
const token = createLlamaCppInferenceRuntimeToken();
|
||||
return {
|
||||
acquire: (signal?: AbortSignal) =>
|
||||
token.acquire({ signal, isLive: () => live, onRestartRequired: () => undefined }),
|
||||
dispose: () => {
|
||||
live = false;
|
||||
token.close();
|
||||
},
|
||||
fail: token.fail,
|
||||
release: token.release,
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
testApi.resetInferenceRuntimeCoordinator();
|
||||
});
|
||||
|
||||
describe("llama.cpp inference runtime coordinator", () => {
|
||||
it("does not reserve native ownership until a runtime acquires it", async () => {
|
||||
createLiveToken();
|
||||
const replacement = createLiveToken();
|
||||
|
||||
await expect(replacement.acquire()).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("hands native ownership to rapid contenders one generation at a time", async () => {
|
||||
const first = createLiveToken();
|
||||
const second = createLiveToken();
|
||||
const third = createLiveToken();
|
||||
await first.acquire();
|
||||
let secondAcquired = false;
|
||||
let thirdAcquired = false;
|
||||
const secondAcquisition = second.acquire().then(() => {
|
||||
secondAcquired = true;
|
||||
});
|
||||
const thirdAcquisition = third.acquire().then(() => {
|
||||
thirdAcquired = true;
|
||||
});
|
||||
|
||||
first.release();
|
||||
await secondAcquisition;
|
||||
expect(secondAcquired).toBe(true);
|
||||
expect(thirdAcquired).toBe(false);
|
||||
|
||||
second.release();
|
||||
await thirdAcquisition;
|
||||
expect(thirdAcquired).toBe(true);
|
||||
});
|
||||
|
||||
it("skips a disposed waiter before handing ownership to the next runtime", async () => {
|
||||
const first = createLiveToken();
|
||||
const disposed = createLiveToken();
|
||||
const replacement = createLiveToken();
|
||||
await first.acquire();
|
||||
const disposedAcquisition = disposed.acquire();
|
||||
let replacementAcquired = false;
|
||||
const replacementAcquisition = replacement.acquire().then(() => {
|
||||
replacementAcquired = true;
|
||||
});
|
||||
|
||||
disposed.dispose();
|
||||
|
||||
await expect(disposedAcquisition).rejects.toThrow("runtime is stopping");
|
||||
expect(replacementAcquired).toBe(false);
|
||||
|
||||
first.release();
|
||||
await expect(replacementAcquisition).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("removes an aborted waiter without letting it claim ownership", async () => {
|
||||
const first = createLiveToken();
|
||||
const aborted = createLiveToken();
|
||||
const replacement = createLiveToken();
|
||||
const abortController = new AbortController();
|
||||
await first.acquire();
|
||||
const abortedAcquisition = aborted.acquire(abortController.signal);
|
||||
const replacementAcquisition = replacement.acquire();
|
||||
|
||||
abortController.abort();
|
||||
first.release();
|
||||
|
||||
await expect(abortedAcquisition).rejects.toThrow();
|
||||
await expect(replacementAcquisition).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("latches cleanup failure for current and future waiters", async () => {
|
||||
const first = createLiveToken();
|
||||
const waiting = createLiveToken();
|
||||
await first.acquire();
|
||||
const waitingAcquisition = waiting.acquire();
|
||||
|
||||
first.fail(new Error("native cleanup failed"));
|
||||
|
||||
await expect(waitingAcquisition).rejects.toBeInstanceOf(LlamaCppInferenceRestartRequiredError);
|
||||
await expect(createLiveToken().acquire()).rejects.toBeInstanceOf(
|
||||
LlamaCppInferenceRestartRequiredError,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user