mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-23 18:38:11 +00:00
fix(scheduler): 安全点重新求值中断队列 + 抢占/背压计数修正 + 停机补终态
对照 docs/zh/input-scheduler-design.md 原文修四处(前两处是真缺陷,后两处是 观测面与设计承诺不一致),均配回归用例: 1. §4.3/§5.2「临界区结束后的第一个安全点重新求值」此前**没有实现**: 全仓唯一的武装点是 registerInterrupt,凡被拦成「入队」的中断只能等当前任务 自然结束。可达症状:WebUI 终止按钮连按两次,第二次落在 2s 抢占冷却窗内 → 入队 → 再也不会被求值。修:runTaskSteps 的安全点先 rearmPending()—— 判据与 registerInterrupt 完全同一套(canPreempt + 冷却 + 临界区闸门)。 2. PreemptsByLevel 的语义是「进入 immediate 槽的次数」,但计数发生在 setImmediateLocked 之前:immediate 是单槽,同一安全点前到达的两条同级中断里 被降级的那条也被计成抢占。修:setImmediateLocked 只在真占住槽时返回 true, 计数随之为真;同时把「降级入队」的责任收归调用方,消除同一任务被入队两次的 隐患(实测该隐患会让中断任务执行两次、Executed 虚高)。 3. 状态面 Preempted 此前拿 Stats.Suspended 顶替,与 preempts_by_level 自相矛盾。 修:Preempted = Σ PreemptsByLevel[1..4]。 4. §4.4/Q4「满时阻塞发送方 + 计数并打日志」只做了阻塞:pumpInbox 满时直接返回, 一个字都不计。修:新增 Stats.Backpressure(+DTO 字段) 与只报一次的状态翻转日志; 同时显式处理 enqueue 返回值(静默丢弃会让同步调用方永久挂起)。 另:Stop() 停机前排空待办——给从未运行与已挂起的、带 ResponseCh 的任务补 skipped 终态,否则 cli/clawhubadapter 这类无超时同步注入方永久挂起(§7 I5、§11.3 X4)。 emitResponse 的 ResponseCh 写入改为非阻塞 + 告警,避免一行写错就卡死调度器 goroutine。 验证:go build/vet 干净;go test -count=1 ./internal/agent/... ./internal/plugin/... ./internal/sdk/... ./cmd/... 全绿;go test -race ./internal/agent/core/ ./internal/sdk/ 干净。 新增 scheduler_rearm_test.go 六个用例(冷却期满重新求值/同级降级不计数/Preempted 求和/ 停机补终态/背压计数与翻转/pumpInbox 满计数)。
This commit is contained in:
@ -331,13 +331,20 @@ func (a *Agent) emitResponse(evt *agentIO.InputEvent, response string) {
|
||||
payload["usage"] = stageCtx.TokenUsage
|
||||
}
|
||||
if evt.ResponseCh != nil {
|
||||
evt.ResponseCh <- &agentIO.OutputEvent{
|
||||
// 非阻塞写:ResponseCh 由同步调用方以 cap=1 创建。按不变量 I5(每任务恰一次
|
||||
// 终态)这里永远写得进去;但一旦哪天写出第二次,阻塞会卡死**调度器 goroutine**
|
||||
// (整个 agent 停摆),而丢弃只是丢一条回执——与 emitSkippedReply 对称。
|
||||
select {
|
||||
case evt.ResponseCh <- &agentIO.OutputEvent{
|
||||
RequestID: evt.RequestID,
|
||||
Target: evt.Source,
|
||||
Type: "text",
|
||||
Payload: payload,
|
||||
Done: true,
|
||||
OutputChannel: ch,
|
||||
}:
|
||||
default:
|
||||
log.Printf("[agent] ResponseCh 已满,终态回执被丢弃(request=%s,可能违反不变量 I5)", evt.RequestID)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user