- Server no longer mirrors app.Runtime services (gov/svc/pwSvc/...): 574 call sites now use accessors in runtime_accessors.go - backgroundLoop (mutex+sync.Once) stops sweeper/publisher/reminder/wsGC goroutines race-free (baseline44161d2areproduced DATA RACE; now green under -race) - goose 00071 duplicate (dialogue merge1da17a28brought two 00071): context-conflict-adjudications renumbered to 00072; dev DB applied only through 00070 so zero data migration - task_finalize tests build minimal app.Runtime; test injection via serverTestOverrides + setOverride* (explicit-nil markers for fail-closed cases)
37 lines
1018 B
Go
37 lines
1018 B
Go
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) })
|
||
}
|
||
}
|