update
This commit is contained in:
70
main/cleanup-epoch.js
Normal file
70
main/cleanup-epoch.js
Normal file
@@ -0,0 +1,70 @@
|
||||
// main/ai.js cancelAll —— 顺序竞争 epoch 闸(audit P1-5)
|
||||
//
|
||||
// 背景:cancelAll 内部三步
|
||||
// 1) for (controller of pending.values()) controller.abort();
|
||||
// 2) pending.clear();
|
||||
// 3) inFlightRequestIds.clear();
|
||||
// 三步非原子。竞态:cancelAll 走到 step 1 刚 abort 完,runEdit 同步阶段
|
||||
// 已经把「新 requestId + 新 controller」加进 pending + inFlightRequestIds →
|
||||
// step 2/3 把这条新 entry 也清掉,runEdit 自以为还在跑,await 不到结果,
|
||||
// 任何同 requestId 重试都找不到 controller。
|
||||
//
|
||||
// 修复:makeCleanupEpoch() 工厂 → { current, snapshot, invalidate, isCurrent }。
|
||||
// - cancelAll 入口 invalidate() → 序号 + 1(任何已 snapshot 的 in-flight 任务
|
||||
// 之后 isCurrent() === false → 自行 bail)
|
||||
// - runEdit 入口 snapshot() 拿当前序号;await 后 isCurrent() === false
|
||||
// (说明本任务期间发生过 cancelAll)→ 不再 apply 任何副作用、直接 return
|
||||
//
|
||||
// 序号从 1 开始自增(0 留给「初始」/「无 epoch」语义)—— isCurrent 比较时
|
||||
// 0 vs 0 也算成立(测试覆盖),但 cancelAll invalidate 之后序号一定 > 0,
|
||||
// 真实 runEdit 拿的 snapshot 至少 1。
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* 创建一个 epoch 计数器对象。cancelAll.invalidate / runEdit.snapshot / runEdit.isCurrent
|
||||
* 配合使用,挡住「cancelAll 中途新请求进入」的竞态。
|
||||
* @returns {{
|
||||
* current: () => number,
|
||||
* snapshot: () => number,
|
||||
* invalidate: () => void,
|
||||
* isCurrent: (snap: number) => boolean,
|
||||
* }}
|
||||
*/
|
||||
function makeCleanupEpoch() {
|
||||
let epoch = 0;
|
||||
return {
|
||||
/**
|
||||
* 当前 epoch 序号。0 = 初始(无 cancelAll 发生过)。
|
||||
* @returns {number}
|
||||
*/
|
||||
current() {
|
||||
return epoch;
|
||||
},
|
||||
/**
|
||||
* 拍下当前 epoch 的快照,用于稍后 isCurrent() 校验。
|
||||
* runEdit 入口调用,await 后比对。
|
||||
* @returns {number}
|
||||
*/
|
||||
snapshot() {
|
||||
return epoch;
|
||||
},
|
||||
/**
|
||||
* 自增 epoch,使所有「更早的 snapshot」isCurrent 失效。
|
||||
* cancelAll 入口调用。
|
||||
*/
|
||||
invalidate() {
|
||||
epoch += 1;
|
||||
},
|
||||
/**
|
||||
* 校验「snap 是否仍是当前 epoch」。
|
||||
* @param {number} snap snapshot() 返回的序号
|
||||
* @returns {boolean} snap === current
|
||||
*/
|
||||
isCurrent(snap) {
|
||||
return epoch === snap;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { makeCleanupEpoch };
|
||||
Reference in New Issue
Block a user