usbip: drop defensive WaitGroup waits in service Close

The server's pendingConnsWG/sessionsWG and the client's wg + 15s shutdown
timer only delayed Close until goroutines settled; they did not own any
resource release. closeConnOnContextDone already closes each accepted or
dialed conn on ctx cancel, so reads/writes error out and the goroutines
exit on their own. Cancel ctx, close the listener and sessions, call
host.Close, return -- no Wait. Keep darwinServerDataSession.wg: it gates
in-flight cgo submit goroutines against device close and is a correctness
barrier, not shutdown defense.
This commit is contained in:
世界
2026-05-17 20:28:43 +08:00
parent 74140d3c67
commit e59e14d89d
4 changed files with 0 additions and 35 deletions
-15
View File
@@ -6,7 +6,6 @@ import (
"context"
"fmt"
"sync"
"time"
"github.com/sagernet/sing-box/adapter"
boxService "github.com/sagernet/sing-box/adapter/service"
@@ -32,7 +31,6 @@ type ClientService struct {
workerAccess sync.Mutex
assignedWorkers []*clientAssignedWorker
allWorkers map[string]context.CancelFunc
wg sync.WaitGroup
controlAccess sync.Mutex
controlSession *clientControlSession
@@ -84,7 +82,6 @@ func (c *ClientService) Start(stage adapter.StartStage) error {
return err
}
c.initializeWorkers()
c.wg.Add(1)
go c.run()
return nil
}
@@ -93,18 +90,6 @@ func (c *ClientService) Close() error {
if c.cancel != nil {
c.cancel()
}
done := make(chan struct{})
go func() {
c.wg.Wait()
close(done)
}()
timer := time.NewTimer(clientShutdownTimeout)
defer timer.Stop()
select {
case <-done:
case <-timer.C:
c.logger.Warn("shutdown timeout; some imports may remain attached")
}
_ = c.host.Close()
return nil
}
-10
View File
@@ -17,7 +17,6 @@ import (
const (
clientReconnectDelay = 5 * time.Second
clientShutdownTimeout = 15 * time.Second
controlPingInterval = 10 * time.Second
controlReadTimeout = 30 * time.Second
controlWriteTimeout = 5 * time.Second
@@ -54,13 +53,11 @@ func (c *ClientService) initializeWorkers() {
c.workerAccess.Unlock()
for _, worker := range workers {
c.wg.Add(1)
go c.runAssignedWorker(worker)
}
}
func (c *ClientService) run() {
defer c.wg.Done()
defer c.stopAllWorkers()
var transientStreak int
@@ -383,8 +380,6 @@ func (c *ClientService) applyMatchedExportsWithRetained(entries []DeviceEntry, k
}
func (c *ClientService) runAssignedWorker(worker *clientAssignedWorker) {
defer c.wg.Done()
var current string
var runnerCancel context.CancelFunc
var runnerDone chan struct{}
@@ -402,7 +397,6 @@ func (c *ClientService) runAssignedWorker(worker *clientAssignedWorker) {
for {
select {
case <-c.ctx.Done():
stopRunner()
return
case desired := <-worker.updates:
if desired == current {
@@ -423,9 +417,7 @@ func (c *ClientService) runAssignedWorker(worker *clientAssignedWorker) {
if worker.target.fixedBusID != "" {
match = option.USBIPDeviceMatch{BusID: worker.target.fixedBusID}
}
c.wg.Add(1)
go func(busid, description string) {
defer c.wg.Done()
defer close(done)
c.runBusIDLoop(runCtx, busid, description)
}(desired, describeMatch(match))
@@ -453,9 +445,7 @@ func (c *ClientService) startRemoteBusIDWorker(busid, description string) {
c.allWorkers[busid] = cancel
c.workerAccess.Unlock()
c.wg.Add(1)
go func() {
defer c.wg.Done()
c.runBusIDLoop(runCtx, busid, description)
}()
}
-8
View File
@@ -35,9 +35,6 @@ type ServerService struct {
sessionsAccess sync.Mutex
sessions map[DataSession]struct{}
sessionsClosed bool
sessionsWG sync.WaitGroup
pendingConnsWG sync.WaitGroup
}
func NewServerService(ctx context.Context, logger log.ContextLogger, tag string, options option.USBIPServerServiceOptions) (adapter.Service, error) {
@@ -128,8 +125,6 @@ func (s *ServerService) Close() error {
for _, session := range sessions {
_ = session.Close()
}
s.pendingConnsWG.Wait()
s.sessionsWG.Wait()
s.reconcileAccess.Lock()
defer s.reconcileAccess.Unlock()
@@ -341,7 +336,6 @@ func (s *ServerService) handleImportReserved(conn net.Conn, busid string, export
// Close may observe a prepared session before Start runs, so
// DataSession implementations must treat Close-before-Start as valid.
s.sessions[session] = struct{}{}
s.sessionsWG.Add(1)
s.sessionsAccess.Unlock()
err = session.Start()
@@ -349,14 +343,12 @@ func (s *ServerService) handleImportReserved(conn net.Conn, busid string, export
s.sessionsAccess.Lock()
delete(s.sessions, session)
s.sessionsAccess.Unlock()
s.sessionsWG.Done()
s.logger.Warn("start data session ", busid, ": ", err)
s.tearDownPreparedSession(busid, session)
return false
}
s.logger.Info("attached ", busid, " to remote ", conn.RemoteAddr())
go func() {
defer s.sessionsWG.Done()
<-session.Done()
s.sessionsAccess.Lock()
delete(s.sessions, session)
-2
View File
@@ -34,13 +34,11 @@ func (s *ServerService) acceptLoop(ln net.Listener) {
s.logger.Error("accept: ", err)
return
}
s.pendingConnsWG.Add(1)
go s.dispatchConn(conn)
}
}
func (s *ServerService) dispatchConn(conn net.Conn) {
defer s.pendingConnsWG.Done()
cancelClose := closeConnOnContextDone(s.ctx, conn)
defer cancelClose()
var prefix [controlPrefaceSize]byte