文件和行号
internal/knight/scheduler.go:Status() 行 497-517、CanPerformTask() 行 526-532;写者对照 Start() 行 190-200、Stop() 行 212-230、Enable() 行 241-249、正确示范 Running() 行 232-237
问题描述
Start()/Stop()/Enable() 在 k.mu 锁内写 k.running、k.lock、k.cfg.Enabled;Running() 正确持锁读。但 Status()(行 502 if !k.running、行 503 if k.lock == nil、行 499 k.cfg.Enabled)和 CanPerformTask()(行 528)全部无锁读同一批字段。Go 内存模型意义上的数据竞态,-race 构建必报。
触发场景
独立复核(sa-100)确认三组跨 goroutine 调用点:
- WebUI 轮询
Status()(cmd/ggcode/daemon.go:1300-1308 HTTP handler goroutine)vs daemon 控制命令调 Start()/Stop()(daemon.go:975/983 另一 handler goroutine)—— 周期性轮询与 enable/stop 并发是常规路径
- TUI 渲染 goroutine 调
Status()(internal/tui/knight_panel.go:372)vs TUI 退出 defer knightAgent.Stop()(cmd/ggcode/root.go:782)
- 工具执行 goroutine 调
CanPerformTask()(PerformSkillAnalysis 路径)vs Stop
本仓库历史上专门修过同文件同类 -race 报警(v1.2.10 analyzedSessions、issue #977),并发调用假设成立。
预期行为 vs 实际行为
预期:所有共享字段读写走 k.mu(同 Running())。
实际:Status()/CanPerformTask() 无锁读,与持锁写构成数据竞态;-race 环境报 DATA RACE,语义上可能显示过期状态(如 k.lock == nil 判定给出错误的 "deferred/stopped")。
修复建议
Status()/CanPerformTask() 用 k.mu 包住对 k.running/k.lock/k.cfg.Enabled 的读取(或提取持锁的 runningLocked() 辅助函数)。注意 Status() 内 k.budget.Used() 若内部也持 k.mu 需在锁外调用,避免重入死锁。
严重程度
medium(无逻辑损坏,但违反项目 race-clean 标准,-race 用户环境必报;同文件有先例修复)
文件和行号
internal/knight/scheduler.go:Status()行 497-517、CanPerformTask()行 526-532;写者对照Start()行 190-200、Stop()行 212-230、Enable()行 241-249、正确示范Running()行 232-237问题描述
Start()/Stop()/Enable()在k.mu锁内写k.running、k.lock、k.cfg.Enabled;Running()正确持锁读。但Status()(行 502if !k.running、行 503if k.lock == nil、行 499k.cfg.Enabled)和CanPerformTask()(行 528)全部无锁读同一批字段。Go 内存模型意义上的数据竞态,-race构建必报。触发场景
独立复核(sa-100)确认三组跨 goroutine 调用点:
Status()(cmd/ggcode/daemon.go:1300-1308HTTP handler goroutine)vs daemon 控制命令调Start()/Stop()(daemon.go:975/983 另一 handler goroutine)—— 周期性轮询与 enable/stop 并发是常规路径Status()(internal/tui/knight_panel.go:372)vs TUI 退出defer knightAgent.Stop()(cmd/ggcode/root.go:782)CanPerformTask()(PerformSkillAnalysis路径)vs Stop本仓库历史上专门修过同文件同类
-race报警(v1.2.10analyzedSessions、issue #977),并发调用假设成立。预期行为 vs 实际行为
预期:所有共享字段读写走
k.mu(同Running())。实际:
Status()/CanPerformTask()无锁读,与持锁写构成数据竞态;-race环境报 DATA RACE,语义上可能显示过期状态(如k.lock == nil判定给出错误的 "deferred/stopped")。修复建议
Status()/CanPerformTask()用k.mu包住对k.running/k.lock/k.cfg.Enabled的读取(或提取持锁的runningLocked()辅助函数)。注意Status()内k.budget.Used()若内部也持k.mu需在锁外调用,避免重入死锁。严重程度
medium(无逻辑损坏,但违反项目 race-clean 标准,-race 用户环境必报;同文件有先例修复)