fix(tools): use sync.Once for thread-safe Stop() in SessionManager

The Stop() method previously used a select/default pattern which was not
safe under concurrent calls — two goroutines could both pass the check
and attempt to close the same channel, causing a panic.

Replace with sync.Once to guarantee exactly-once close semantics,
matching the documented contract of being safe for concurrent use.

Review feedback: afjcjsbx
This commit is contained in:
程智超0668000959
2026-06-02 19:45:31 +08:00
parent bb57e0498c
commit e70a9fca7c
2 changed files with 7 additions and 9 deletions
+6 -8
View File
@@ -172,6 +172,7 @@ type SessionManager struct {
mu sync.RWMutex
sessions map[string]*ProcessSession
stopCh chan struct{}
stopOnce sync.Once
}
func NewSessionManager() *SessionManager {
@@ -197,16 +198,13 @@ func NewSessionManager() *SessionManager {
return sm
}
// Stop shuts down the background cleanup goroutine. Safe to call multiple times.
// After Stop returns, the SessionManager is still usable — only the cleanup
// goroutine is terminated.
// Stop shuts down the background cleanup goroutine. Safe to call multiple
// times from concurrent goroutines. After Stop returns, the SessionManager
// is still usable — only the cleanup goroutine is terminated.
func (sm *SessionManager) Stop() {
select {
case <-sm.stopCh:
// already closed
default:
sm.stopOnce.Do(func() {
close(sm.stopCh)
}
})
}
// cleanupOldSessions removes sessions that are done and older than 30 minutes
+1 -1
View File
@@ -1,4 +1,4 @@
package tools
package tools
import (
"context"