From edca948e7f4e7042e2402a447397a4d2e179cdf0 Mon Sep 17 00:00:00 2001 From: singchia Date: Fri, 24 Apr 2026 23:53:44 +0800 Subject: [PATCH] fix(scheduler): atomic-read counters in control loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit control() reads countIncomingReqs and countProcessedReqs with plain loads while worker goroutines (and PublishRequest) update them via atomic.AddInt64. Under the race detector this consistently trips a data-race warning — surfaced downstream in singchia/geminio's race CI. Snapshot each counter with a single atomic.LoadInt64 so both the diff and the baseline update see the same value, and no plain read races with a concurrent atomic write. No behavioral change on non-race builds. Co-Authored-By: Claude Opus 4.7 (1M context) --- pkg/scheduler/scheduler.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go index 031d662..de3857b 100644 --- a/pkg/scheduler/scheduler.go +++ b/pkg/scheduler/scheduler.go @@ -127,10 +127,16 @@ func (s *Scheduler) control() { return } numActives := atomic.LoadInt64(&s.numActives) - incomingReqsDiff := s.countIncomingReqs - s.countIncomingReqsL - processedReqsDiff := s.countProcessedReqs - s.countProcessedReqsL - s.countIncomingReqsL = s.countIncomingReqs - s.countProcessedReqsL = s.countProcessedReqs + // Snapshot the two counters with a single atomic load each so + // the diff and the baseline update see the same value. Worker + // goroutines write these via atomic.AddInt64; plain reads here + // are a data race under -race. + incoming := atomic.LoadInt64(&s.countIncomingReqs) + processed := atomic.LoadInt64(&s.countProcessedReqs) + incomingReqsDiff := incoming - s.countIncomingReqsL + processedReqsDiff := processed - s.countProcessedReqsL + s.countIncomingReqsL = incoming + s.countProcessedReqsL = processed s.runtimeLock.RLock() shift := s.strategy.ExpandOrShrink(incomingReqsDiff, processedReqsDiff, numActives)