mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-03 15:53:56 +00:00
feat(scheduler)!: 中断/排队两类别模型 + 插件声明 L1-L3、L4 内核独占
用户澄清推翻了早期设计的三处前提,本提交按新模型重做调度核心(行为有意变化): 1) 类别由注入 API 决定,与通道名无关 - InjectInterrupt* -> TaskInterrupt(带级别,可被严格更高级中断打断) - InjectText*/InjectInputSync*/内核自循环 -> TaskQueued(无级别,可被任何中断打断) - 删除按通道名推断的 taskLevel():qq 走 InjectInterruptTextOpts,本就是中断 2) 级别只属于中断 - 插件在 InjectOptions.Priority 声明 L1-L3(空/非法降级 L1,声明 L4 夹到 L3) - L4 内核独占:新增 raiseKernelInterrupt(panic / selfip);requestKernelPreempt 不夹取 - panic 现在产生一条带 kernel 标记的 L4 中断;L4 自身 panic 不再产生新 L4(防自我放大) 3) 选择结构:四容器固定次序,删除统一比较器 - immediate(抢占者立即运行)-> 中断队列 L4..L1 -> 栈顶(与队头比级别) -> 排队 FIFO - 删除 pickTaskIndex/taskBefore 与“同级 pending 优先”补丁(根因是抢占者进了队列) - 中断栈上界改为结构推论 = 4(= 中断级数);删除“超限转 pendingInterrupts”降级 公开 SDK(feature 分支有意新增,纯追加):InjectOptions.Priority + PriorityL1/2/3; 内核 io / proc 桥 / 插件模板同步透传。 设计稿 §2/§3/§4.1/§6.3/§9/§11/§12/§13/§15 按新模型重写。 验收:go build/vet 干净;go test ./... 37 包 ok 0 FAIL;-race 全绿; e2e(抢占-挂起-恢复)+ 压力(200 排队 + 50 中断,L1/L2/L3 轮转)通过。
This commit is contained in:
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func TestStarvation_EffectiveLevelPromotion(t *testing.T) {
|
||||
base := &Task{Level: LevelBackground}
|
||||
base := &Task{Class: TaskInterrupt, Level: LevelBackground}
|
||||
if got := effectiveLevel(base); got != LevelBackground {
|
||||
t.Fatalf("未抢占时有效级=%v,期望 L1", got)
|
||||
}
|
||||
@ -33,7 +33,7 @@ func TestStarvation_EffectiveLevelPromotion(t *testing.T) {
|
||||
}
|
||||
|
||||
// 封顶 L4:L3 任务被多次抢占也不会超过紧急级。
|
||||
high := &Task{Level: LevelInteractive, PreemptCount: 99}
|
||||
high := &Task{Class: TaskInterrupt, Level: LevelInteractive, PreemptCount: 99}
|
||||
if got := effectiveLevel(high); got != LevelCritical {
|
||||
t.Fatalf("L3 提升后应封顶为 L4,实际 %v", got)
|
||||
}
|
||||
@ -42,8 +42,8 @@ func TestStarvation_EffectiveLevelPromotion(t *testing.T) {
|
||||
func TestStarvation_CooldownBlocksImmediateRepreempt(t *testing.T) {
|
||||
a := newPreemptAgent(t, newPreemptProvider())
|
||||
|
||||
low := &Task{ID: 1, Level: LevelBackground, EnqueuedAt: time.Now()}
|
||||
a.sched.enqueue(low)
|
||||
low := &Task{ID: 1, Class: TaskInterrupt, Level: LevelBackground, EnqueuedAt: time.Now()}
|
||||
a.sched.immediate = low
|
||||
a.sched.nextRef() // running = low
|
||||
|
||||
e1, _ := textEvent("qq", "第一次打断")
|
||||
@ -64,7 +64,7 @@ func TestStarvation_CooldownBlocksImmediateRepreempt(t *testing.T) {
|
||||
a.sched.mu.Unlock()
|
||||
|
||||
e2, _ := textEvent("cli", "冷却期内的紧急打断")
|
||||
if a.sched.requestPreempt(e2, LevelCritical) {
|
||||
if a.sched.requestKernelPreempt(e2) {
|
||||
t.Fatal("抢占冷却期内不得再抢占")
|
||||
}
|
||||
if a.sched.preemptGrantedFor() {
|
||||
@ -75,8 +75,8 @@ func TestStarvation_CooldownBlocksImmediateRepreempt(t *testing.T) {
|
||||
func TestStarvation_PromotionBlocksSameLevelPreempt(t *testing.T) {
|
||||
a := newPreemptAgent(t, newPreemptProvider())
|
||||
|
||||
low := &Task{ID: 1, Level: LevelBackground, EnqueuedAt: time.Now()}
|
||||
a.sched.enqueue(low)
|
||||
low := &Task{ID: 1, Class: TaskInterrupt, Level: LevelBackground, EnqueuedAt: time.Now()}
|
||||
a.sched.immediate = low
|
||||
a.sched.nextRef()
|
||||
// 模拟「已被抢占过一次」:有效级 = L2。
|
||||
low.PreemptCount = 1
|
||||
@ -96,23 +96,29 @@ func TestStarvation_PromotionBlocksSameLevelPreempt(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// 选择函数必须用有效级:被抢占过的任务在排队时应当优先于同级/更低的任务。
|
||||
func TestStarvation_SelectionUsesEffectiveLevel(t *testing.T) {
|
||||
base := time.Now()
|
||||
promoted := &Task{ID: 1, Level: LevelBackground, PreemptCount: 2, EnqueuedAt: base} // 有效 L3
|
||||
normal := &Task{ID: 2, Level: LevelMessage, EnqueuedAt: base.Add(time.Second)} // L2
|
||||
// 提升必须真的进入抢占判据,而不只是一个数学性质:
|
||||
// 被抢占过一次的 L1 中断(有效 L2)应当顶住同级 L2 流的再次抢占。
|
||||
func TestStarvation_PromotionIsVisibleInSelection(t *testing.T) {
|
||||
a := newPreemptAgent(t, newPreemptProvider())
|
||||
|
||||
if !taskBefore(promoted, normal) {
|
||||
t.Fatal("被抢占 2 次的 L1(有效 L3)应先于 L2 执行")
|
||||
}
|
||||
if taskBefore(normal, promoted) {
|
||||
t.Fatal("选择函数不得只看基础级")
|
||||
// 栈里放一个「被抢占过一次的 L1」:有效级 L2。
|
||||
a.sched.suspend(&Task{ID: 1, Class: TaskInterrupt, Level: LevelBackground, PreemptCount: 1},
|
||||
a.newTaskFrame("A", a.stageCtxFromInput("A", "", "")))
|
||||
|
||||
// 队列里来一个 L2:有效级持平(2 vs 2)→ 不得越过栈顶。
|
||||
evt, _ := textEvent("qq", "L2 中断")
|
||||
a.sched.registerInterrupt(newInterruptTask(evt, LevelMessage))
|
||||
if _, _, kind := a.sched.nextRef(); kind != nextSuspended {
|
||||
t.Fatalf("有效级持平应恢复栈顶,kind=%v", kind)
|
||||
}
|
||||
|
||||
// 提升不改变调度器自身的排序稳定性:同为有效级时按入队时刻。
|
||||
a := &Task{ID: 3, Level: LevelBackground, PreemptCount: 1, EnqueuedAt: base.Add(2 * time.Second)} // 有效 L2
|
||||
b := &Task{ID: 4, Level: LevelMessage, EnqueuedAt: base.Add(time.Second)} // L2,更早
|
||||
if !taskBefore(b, a) {
|
||||
t.Fatal("同有效级时应先到先服务")
|
||||
// 队列里来一个 L3:严格大于 → 队头优先。
|
||||
// PreemptCount 从 0 起:suspend 内部会 +1 → 有效级 L2(正好用来卡 L2 持平)。
|
||||
a.sched.suspend(&Task{ID: 2, Class: TaskInterrupt, Level: LevelBackground},
|
||||
a.newTaskFrame("B", a.stageCtxFromInput("B", "", "")))
|
||||
evt2, _ := textEvent("cli", "L3 中断")
|
||||
a.sched.registerInterrupt(newInterruptTask(evt2, LevelInteractive))
|
||||
if _, _, kind := a.sched.nextRef(); kind != nextInterrupt {
|
||||
t.Fatalf("L3 > 有效 L2 应取中断队列,kind=%v", kind)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user