From bab78de6a46fa585e85c6e71e4d4b264ffc1a878 Mon Sep 17 00:00:00 2001 From: Together Date: Thu, 12 Feb 2026 17:56:04 +0800 Subject: [PATCH] fix(heartbeat): resolve bug where service could never start HeartbeatService.Start() always returned early because running() checked stopChan closure state, which is "open" (= true) for a newly created service. This caused Start() to interpret a fresh service as "already running" and skip launching the goroutine. Introduce a `started` bool field to separate "has been started" from "has not been stopped", fixing both the start failure and a potential double-close panic on Stop(). --- pkg/heartbeat/service.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/heartbeat/service.go b/pkg/heartbeat/service.go index ba85d71..0f564bf 100644 --- a/pkg/heartbeat/service.go +++ b/pkg/heartbeat/service.go @@ -14,6 +14,7 @@ type HeartbeatService struct { interval time.Duration enabled bool mu sync.RWMutex + started bool stopChan chan struct{} } @@ -31,7 +32,7 @@ func (hs *HeartbeatService) Start() error { hs.mu.Lock() defer hs.mu.Unlock() - if hs.running() { + if hs.started { return nil } @@ -39,6 +40,7 @@ func (hs *HeartbeatService) Start() error { return fmt.Errorf("heartbeat service is disabled") } + hs.started = true go hs.runLoop() return nil @@ -48,10 +50,11 @@ func (hs *HeartbeatService) Stop() { hs.mu.Lock() defer hs.mu.Unlock() - if !hs.running() { + if !hs.started { return } + hs.started = false close(hs.stopChan) }