mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-01 06:47:29 +00:00
refactor(agent/core): 中断队列归还调度器,中断管理器只留现场保护
用户裁定:「中断队列就该调度器管理啊,中断管理器只负责进入中断的任务」。 上一版我把 interruptQueues/immediate 也搬进了中断管理器,这是错的: 排队是**调度职责**——判断该不该打断、打断不了先排队、按什么次序再取出来, 全是调度策略,不是"进入中断"本身。 ## 本次改动 中断管理器(interrupt.go)现在只有: - 中断栈 suspendStack(**唯一操作者**:压栈/弹栈都在其内) - 中断屏蔽 critical(对应 CPU 关中断) - 让位信号原语 preemptArmed/preemptLevel - 栈状态钩子 stackNotify 迁回调度器(scheduler.go): - interruptQueues[5] / immediate 两个字段 - highestInterruptLocked / popInterruptLocked / interruptCountLocked - enqueueInterruptLocked / installImmediateLocked ## 新增边界判据 TestInvariant_InterruptManagerHoldsOnlyStackState 用**反射白名单**钉住边界: interruptState 只允许 6 个字段(栈/屏蔽/让位×2/上界/钩子)。 反向确认 interruptQueues 与 immediate 确实在 scheduler 上——避免"两边都没有"的假绿。 变异验证:把 interruptQueues 塞回 interruptState ⇒ 判据立刻变红 ("中断管理器不应持有这些字段:[interruptQueues]")。 ## 验证 - go build ./... 通过 - go test ./internal/agent/core/ 通过 - go test -race ./internal/agent/core/ 通过 - 12 条不变量判据全绿 ## 顺带排查:/tmp 是 tmpfs 且已 91% `df` 显示 /tmp 是 8.8G tmpfs,Go 链接期临时文件把它填满, 导致并发构建出现 write ... no space left on device(表现为 某包 `FAIL 0.008s`——不是代码问题)。已改用 TMPDIR=/var/tmp/gobuild(根分区 112G)。
This commit is contained in:
@ -215,9 +215,67 @@ func clampPluginLevel(l Level) Level {
|
||||
return l
|
||||
}
|
||||
|
||||
// ── 中断队列操作(归调度器)──
|
||||
|
||||
func (s *scheduler) highestInterruptLocked() (*Task, Level) {
|
||||
for lv := LevelCritical; lv >= LevelBackground; lv-- {
|
||||
if q := s.interruptQueues[lv]; len(q) > 0 {
|
||||
return q[0], lv
|
||||
}
|
||||
}
|
||||
return nil, 0
|
||||
}
|
||||
|
||||
func (s *scheduler) popInterruptLocked(lv Level) {
|
||||
s.interruptQueues[lv] = s.interruptQueues[lv][1:]
|
||||
}
|
||||
|
||||
func (s *scheduler) interruptCountLocked() int {
|
||||
n := 0
|
||||
for lv := LevelBackground; lv <= LevelCritical; lv++ {
|
||||
n += len(s.interruptQueues[lv])
|
||||
}
|
||||
if s.immediate != nil {
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (s *scheduler) enqueueInterruptLocked(t *Task) {
|
||||
s.allocateIDLocked(t)
|
||||
if s.interruptCountLocked() >= s.maxQueue {
|
||||
for lv := LevelBackground; lv <= LevelCritical; lv++ {
|
||||
if len(s.interruptQueues[lv]) > 0 {
|
||||
s.popInterruptLocked(lv)
|
||||
s.stats.Rejected++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
lv := t.Level
|
||||
if lv < LevelBackground || lv > LevelCritical {
|
||||
lv = DefaultLevel
|
||||
}
|
||||
s.interruptQueues[lv] = append(s.interruptQueues[lv], t)
|
||||
s.stats.Enqueued++
|
||||
}
|
||||
|
||||
func (s *scheduler) installImmediateLocked(t *Task) *Task {
|
||||
var demoted *Task
|
||||
if s.immediate != nil {
|
||||
// 被顶掉的抢占者降级入队,**不丢**(不变量 I5)。
|
||||
demoted = s.immediate
|
||||
s.enqueueInterruptLocked(demoted)
|
||||
}
|
||||
s.allocateIDLocked(t)
|
||||
s.stats.Enqueued++
|
||||
s.immediate = t
|
||||
return demoted
|
||||
}
|
||||
|
||||
// ── 中断管理器(interrupt.go)──
|
||||
// 只提供中断队列/中断栈/immediate 槽/让位信号等原语;
|
||||
// 优先级比较由本文件的 canPreempt/effectiveLevel 提供,中断管理器不自行判断。
|
||||
// 只负责**进入中断**:现场压栈/弹栈(中断栈)、中断屏蔽、让位信号原语。
|
||||
// 中断队列、优先级比较、该不该打断、该恢复谁——全部在本文件(调度器)。
|
||||
|
||||
// setImmediateLocked 判断"该不该由 t 占 immediate 槽",判据是策略,故归调度器。
|
||||
//
|
||||
@ -236,8 +294,8 @@ func (s *scheduler) setImmediateLocked(t *Task) bool {
|
||||
|
||||
// ── 中断登记与恢复决策(调度策略,归调度器)──
|
||||
//
|
||||
// 这些函数决定"能不能打断、恢复谁"——是**策略**,住在这里。
|
||||
// 它们调用的入队/弹栈/压栈是**机制**,住在 interrupt.go。
|
||||
// 这些函数决定"能不能打断、排到哪条队列、恢复谁"——全是调度策略。
|
||||
// 它们只把"保存现场/恢复现场"这一件事交给 interrupt.go。
|
||||
|
||||
func (s *scheduler) registerInterrupt(t *Task) bool {
|
||||
s.mu.Lock()
|
||||
@ -495,6 +553,20 @@ type scheduler struct {
|
||||
stats SchedulerStats
|
||||
maxQueue int
|
||||
|
||||
// ── 中断队列(归调度器)──
|
||||
//
|
||||
// 中断管理器只负责"进入中断"(现场压栈)与中断栈;**排队**是调度职责:
|
||||
// 判断该不该打断、打断不了就先排队、按什么次序再取出来,全是调度策略。
|
||||
|
||||
// interruptQueues[level]:四条**中断队列**(level 1..4),同级 FIFO。
|
||||
// 未能立即抢占的中断(级别不足,或运行任务在临界区)按级别入队。
|
||||
interruptQueues [5][]*Task
|
||||
|
||||
// immediate:已判定的最高优先待运行者。抢占必须**立即生效**,
|
||||
// 所以它不经队列,在下一个安全点直接运行。
|
||||
// 这也消除了"抢占者与被抢占者同级"的比较问题——抢占者不需和栈顶比。
|
||||
immediate *Task
|
||||
|
||||
// interrupt:中断子系统的全部状态(见 interrupt.go)。
|
||||
//
|
||||
// **内嵌**而非具名字段:内嵌后字段提升,所有 `s.interruptQueues` /
|
||||
|
||||
Reference in New Issue
Block a user