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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user