fix(engine): abort signal races gen.next() to fix flaky kill test (#209)
Root cause: executeThread awaited gen.next() without racing against the abort signal. When a workflow bundle awaited a long setTimeout between yields, the engine could not respond to kill until the Promise resolved — causing the kill test to flake when the thread completed before kill arrived. Fix: Promise.race gen.next() with an abort listener so kill takes effect immediately, even mid-yield. Also move the bundle's delay to after the first yield (between planner and coder) to ensure the thread is killable while running. Closes #209
This commit is contained in:
@@ -70,10 +70,10 @@ const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
|
||||
const abortablePlannerBundleSource = `${threadFixtureDescriptor}
|
||||
${wfPutImport}
|
||||
export const run = async function* (input, options) {
|
||||
await new Promise((r) => setTimeout(r, 600));
|
||||
const cas = options.cas;
|
||||
let h = await putContentMerkleNode(cas, "plan");
|
||||
yield { role: "planner", contentHash: h, meta: { plan: input.prompt }, refs: [h] };
|
||||
await new Promise((r) => setTimeout(r, 10000));
|
||||
h = await putContentMerkleNode(cas, "code");
|
||||
yield { role: "coder", contentHash: h, meta: { diff: "y" }, refs: [h] };
|
||||
return { returnCode: 0, summary: "done" };
|
||||
|
||||
@@ -299,7 +299,37 @@ async function driveWorkflowGenerator(params: {
|
||||
});
|
||||
}
|
||||
|
||||
const iterResult = await gen.next();
|
||||
const iterResult = await Promise.race([
|
||||
gen.next(),
|
||||
new Promise<never>((_, reject) => {
|
||||
if (executeOptions.signal.aborted) {
|
||||
reject(new DOMException("The operation was aborted", "AbortError"));
|
||||
return;
|
||||
}
|
||||
executeOptions.signal.addEventListener(
|
||||
"abort",
|
||||
() => reject(new DOMException("The operation was aborted", "AbortError")),
|
||||
{ once: true },
|
||||
);
|
||||
}),
|
||||
]).catch((e) => {
|
||||
if (e instanceof DOMException && e.name === "AbortError") {
|
||||
return { done: true as const, value: { returnCode: 130, summary: "thread aborted" } };
|
||||
}
|
||||
throw e;
|
||||
});
|
||||
|
||||
if (executeOptions.signal.aborted || (iterResult.done && iterResult.value.returnCode === 130)) {
|
||||
return await finalizeAbortedThread({
|
||||
cas,
|
||||
bundleDir,
|
||||
threadId,
|
||||
startHash,
|
||||
chain,
|
||||
logger,
|
||||
abortLogTag: "H4KQ7RW3",
|
||||
});
|
||||
}
|
||||
|
||||
if (iterResult.done) {
|
||||
logger("F3HN8QKP", `thread ${threadId} generator finished`);
|
||||
|
||||
Reference in New Issue
Block a user