Skip to content

Commit 180bcad

Browse files
committed
net/http: wait for listeners to exit in Server.Close and Shutdown
Avoid race conditions when a new connection is accepted just after Server.Close or Server.Shutdown is called by waiting for the listener goroutines to exit before proceeding to clean up active connections. No test because the mechanism required to trigger the race condition reliably requires such tight coupling to the Server internals that any test would be quite fragile in the face of reasonable refactorings. Fixes #48642 Updates #33313, #36819 Change-Id: I109a93362680991bf298e0a95637595dcaa884af Reviewed-on: https://go-review.googlesource.com/c/go/+/409537 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]> Reviewed-by: Bryan Mills <[email protected]>
1 parent 14abe8a commit 180bcad

File tree

1 file changed

+15
-7
lines changed

1 file changed

+15
-7
lines changed

src/net/http/server.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2690,6 +2690,8 @@ type Server struct {
26902690
activeConn map[*conn]struct{}
26912691
doneChan chan struct{}
26922692
onShutdown []func()
2693+
2694+
listenerGroup sync.WaitGroup
26932695
}
26942696

26952697
func (s *Server) getDoneChan() <-chan struct{} {
@@ -2732,6 +2734,15 @@ func (srv *Server) Close() error {
27322734
defer srv.mu.Unlock()
27332735
srv.closeDoneChanLocked()
27342736
err := srv.closeListenersLocked()
2737+
2738+
// Unlock srv.mu while waiting for listenerGroup.
2739+
// The group Add and Done calls are made with srv.mu held,
2740+
// to avoid adding a new listener in the window between
2741+
// us setting inShutdown above and waiting here.
2742+
srv.mu.Unlock()
2743+
srv.listenerGroup.Wait()
2744+
srv.mu.Lock()
2745+
27352746
for c := range srv.activeConn {
27362747
c.rwc.Close()
27372748
delete(srv.activeConn, c)
@@ -2778,6 +2789,7 @@ func (srv *Server) Shutdown(ctx context.Context) error {
27782789
go f()
27792790
}
27802791
srv.mu.Unlock()
2792+
srv.listenerGroup.Wait()
27812793

27822794
pollIntervalBase := time.Millisecond
27832795
nextPollInterval := func() time.Duration {
@@ -2794,7 +2806,7 @@ func (srv *Server) Shutdown(ctx context.Context) error {
27942806
timer := time.NewTimer(nextPollInterval())
27952807
defer timer.Stop()
27962808
for {
2797-
if srv.closeIdleConns() && srv.numListeners() == 0 {
2809+
if srv.closeIdleConns() {
27982810
return lnerr
27992811
}
28002812
select {
@@ -2817,12 +2829,6 @@ func (srv *Server) RegisterOnShutdown(f func()) {
28172829
srv.mu.Unlock()
28182830
}
28192831

2820-
func (s *Server) numListeners() int {
2821-
s.mu.Lock()
2822-
defer s.mu.Unlock()
2823-
return len(s.listeners)
2824-
}
2825-
28262832
// closeIdleConns closes all idle connections and reports whether the
28272833
// server is quiescent.
28282834
func (s *Server) closeIdleConns() bool {
@@ -3157,8 +3163,10 @@ func (s *Server) trackListener(ln *net.Listener, add bool) bool {
31573163
return false
31583164
}
31593165
s.listeners[ln] = struct{}{}
3166+
s.listenerGroup.Add(1)
31603167
} else {
31613168
delete(s.listeners, ln)
3169+
s.listenerGroup.Done()
31623170
}
31633171
return true
31643172
}

0 commit comments

Comments
 (0)