package server import "sync" // backgroundLoop 收口 Server 后台循环的停止语义(修 DATA RACE: Shutdown 与 // sweeper/publisher/reminder/GC goroutine 对 stop 通道的并发 close/nil 写)。 // 通道仅在启动 goroutine 内一次创建并被该 goroutine 独占持有; // Shutdown 经 stopOnce 通知,不触碰通道字段本身。 type backgroundLoop struct { mu sync.Mutex stopCh chan struct{} stopOnce sync.Once stopped bool } // start 登记停止通道;返回值是否实际启动(幂等:已存在/已停止返回 false)。 func (l *backgroundLoop) start() (chan struct{}, bool) { l.mu.Lock() defer l.mu.Unlock() if l.stopCh != nil || l.stopped { return nil, false } l.stopCh = make(chan struct{}) return l.stopCh, true } // stop 通知循环退出并防止后续再启动;可安全多次调用。 func (l *backgroundLoop) stop() { l.mu.Lock() ch := l.stopCh l.stopped = true l.mu.Unlock() if ch != nil { l.stopOnce.Do(func() { close(ch) }) } }