Bound checked candidate metadata and restricted invocation resources
This commit is contained in:
@@ -19,6 +19,7 @@ async function fixture(source, options = {}) {
|
||||
const io = { stdin: new PassThrough(), stdout: new PassThrough(), exitCode: 0 };
|
||||
const waiting = [],
|
||||
queued = [];
|
||||
const usage = [];
|
||||
let buffer = "";
|
||||
io.stdout.on("data", (chunk) => {
|
||||
buffer += chunk;
|
||||
@@ -26,6 +27,10 @@ async function fixture(source, options = {}) {
|
||||
while ((index = buffer.indexOf("\n")) >= 0) {
|
||||
const value = JSON.parse(buffer.slice(0, index));
|
||||
buffer = buffer.slice(index + 1);
|
||||
if (value.kind === "usage") {
|
||||
usage.push(value);
|
||||
continue;
|
||||
}
|
||||
if (waiting.length) waiting.shift()(value);
|
||||
else queued.push(value);
|
||||
}
|
||||
@@ -50,7 +55,7 @@ async function fixture(source, options = {}) {
|
||||
);
|
||||
assert.deepEqual(await receive(), { abi, kind: "ready", exports: ["test"] });
|
||||
const send = (value) => io.stdin.write(JSON.stringify({ abi, ...value }) + "\n");
|
||||
const start = (input = null) => {
|
||||
const start = (input = null, budget) => {
|
||||
const invocation = "invoke-" + ++next,
|
||||
frame = "frame-" + next;
|
||||
send({
|
||||
@@ -62,6 +67,7 @@ async function fixture(source, options = {}) {
|
||||
receiver: null,
|
||||
input,
|
||||
effect: "snapshot",
|
||||
...(budget ? { budget } : {}),
|
||||
});
|
||||
return { invocation, frame };
|
||||
};
|
||||
@@ -70,6 +76,7 @@ async function fixture(source, options = {}) {
|
||||
receive,
|
||||
send,
|
||||
start,
|
||||
usage,
|
||||
async close() {
|
||||
await service.close();
|
||||
await rm(directory, { recursive: true, force: true });
|
||||
@@ -78,6 +85,94 @@ async function fixture(source, options = {}) {
|
||||
}
|
||||
const registry = (code) => `export const registry={exports:['test'],async invoke(request,channel){${code}}};`;
|
||||
|
||||
test("parallel frames route independently and cancellation cannot retire a neighbor", async () => {
|
||||
const f = await fixture(
|
||||
registry("return await channel({portId:'read',action:{kind:'state',primitive:'read'},input:request.input});"),
|
||||
);
|
||||
try {
|
||||
const first = f.start("a"),
|
||||
second = f.start("b");
|
||||
const ports = new Map([await f.receive(), await f.receive()].map((port) => [port.invocation, port]));
|
||||
assert.equal(ports.get(first.invocation).input, "a");
|
||||
assert.equal(ports.get(second.invocation).input, "b");
|
||||
f.send({ kind: "cancel", ...first });
|
||||
assert.equal((await f.receive()).kind, "cancelled");
|
||||
f.send({
|
||||
kind: "port-result",
|
||||
...first,
|
||||
sequence: ports.get(first.invocation).sequence,
|
||||
status: "returned",
|
||||
output: "late",
|
||||
});
|
||||
f.send({
|
||||
kind: "port-result",
|
||||
...second,
|
||||
sequence: ports.get(second.invocation).sequence,
|
||||
status: "returned",
|
||||
output: "survived",
|
||||
});
|
||||
const result = await f.receive();
|
||||
assert.equal(result.invocation, second.invocation);
|
||||
assert.equal(result.output, "survived");
|
||||
assert.equal(f.io.exitCode, 0);
|
||||
} finally {
|
||||
await f.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("A to B to A uses reserved nested capacity while both root lanes are saturated", async () => {
|
||||
const source = registry(
|
||||
"if(request.input==='leaf')return 'done'; return await channel({portId:'next',action:{kind:'state',primitive:'read'},input:request.input});",
|
||||
);
|
||||
const a = await fixture(source, { maxActive: 1, maxNested: 1, maxQueued: 1 });
|
||||
const b = await fixture(source, { maxActive: 1, maxNested: 1, maxQueued: 1 });
|
||||
const budget = (depth) => ({ root: "root-chain", depth, deadlineMs: 2000, calls: 20 });
|
||||
try {
|
||||
const parent = a.start("B", budget(1)),
|
||||
aPort = await a.receive();
|
||||
const neighbor = b.start("held", { ...budget(1), root: "other-root" });
|
||||
await b.receive();
|
||||
const child = b.start("A", budget(2)),
|
||||
bPort = await b.receive();
|
||||
const leaf = a.start("leaf", budget(3));
|
||||
const leafResult = await a.receive();
|
||||
assert.equal(leafResult.invocation, leaf.invocation);
|
||||
assert.equal(leafResult.output, "done");
|
||||
b.send({ kind: "port-result", ...child, sequence: bPort.sequence, status: "returned", output: leafResult.output });
|
||||
const childResult = await b.receive();
|
||||
a.send({
|
||||
kind: "port-result",
|
||||
...parent,
|
||||
sequence: aPort.sequence,
|
||||
status: "returned",
|
||||
output: childResult.output,
|
||||
});
|
||||
assert.equal((await a.receive()).output, "done");
|
||||
b.send({ kind: "cancel", ...neighbor });
|
||||
assert.equal((await b.receive()).kind, "cancelled");
|
||||
} finally {
|
||||
await a.close();
|
||||
await b.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("the remaining root wall budget overrides a longer adapter deadline", async () => {
|
||||
const f = await fixture(
|
||||
registry("return await channel({portId:'held',action:{kind:'state',primitive:'read'},input:null});"),
|
||||
{ deadlineMs: 2000 },
|
||||
);
|
||||
try {
|
||||
const started = performance.now();
|
||||
f.start(null, { root: "short-root", depth: 2, deadlineMs: 150, calls: 10 });
|
||||
await f.receive();
|
||||
const result = await f.receive();
|
||||
assert.equal(result.error.code, "RESTRICTED_DEADLINE");
|
||||
assert.ok(performance.now() - started < 1000);
|
||||
} finally {
|
||||
await f.close();
|
||||
}
|
||||
});
|
||||
|
||||
test("real engine returns results and resets all globals between frames", async () => {
|
||||
const f = await fixture(
|
||||
registry(
|
||||
@@ -222,6 +317,10 @@ test("initialization and regex loops are killed, then a fresh frame can start",
|
||||
f.start();
|
||||
const result = await f.receive();
|
||||
assert.equal(result.error.code, "RESTRICTED_DEADLINE");
|
||||
assert.ok(
|
||||
f.usage.some((value) => value.cpuMicroseconds >= 50000),
|
||||
"External watchdog did not account spinning engine CPU",
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
await f.close();
|
||||
@@ -268,7 +367,7 @@ test("cancellation during asynchronous preparation leaves no running frame", asy
|
||||
test("bounded FIFO rejects overload without losing active or queued frames", async () => {
|
||||
const f = await fixture(
|
||||
registry("return await channel({portId:'read',action:{kind:'state',primitive:'read'},input:request.input});"),
|
||||
{ maxQueued: 2 },
|
||||
{ maxQueued: 2, maxActive: 1 },
|
||||
);
|
||||
try {
|
||||
const first = f.start(1),
|
||||
@@ -307,7 +406,7 @@ test("bounded FIFO rejects overload without losing active or queued frames", asy
|
||||
test("queue wait consumes the deadline and cancelled work never reaches its ports", async () => {
|
||||
const f = await fixture(
|
||||
registry("return await channel({portId:'read',action:{kind:'state',primitive:'read'},input:null});"),
|
||||
{ deadlineMs: 350 },
|
||||
{ deadlineMs: 350, maxActive: 1 },
|
||||
);
|
||||
try {
|
||||
const first = f.start();
|
||||
|
||||
Reference in New Issue
Block a user