diff options
-rw-r--r-- | src/net/http/http_test.go | 5 | ||||
-rw-r--r-- | src/net/http/server.go | 28 |
2 files changed, 21 insertions, 12 deletions
diff --git a/src/net/http/http_test.go b/src/net/http/http_test.go index 49c2b4196a..3f1d7cee71 100644 --- a/src/net/http/http_test.go +++ b/src/net/http/http_test.go @@ -13,13 +13,8 @@ import ( "os/exec" "reflect" "testing" - "time" ) -func init() { - shutdownPollInterval = 5 * time.Millisecond -} - func TestForeachHeaderElement(t *testing.T) { tests := []struct { in string diff --git a/src/net/http/server.go b/src/net/http/server.go index ba473d14f5..4776d960e5 100644 --- a/src/net/http/server.go +++ b/src/net/http/server.go @@ -15,6 +15,7 @@ import ( "fmt" "io" "log" + "math/rand" "net" "net/textproto" "net/url" @@ -2691,14 +2692,14 @@ func (srv *Server) Close() error { return err } -// shutdownPollInterval is how often we poll for quiescence -// during Server.Shutdown. This is lower during tests, to -// speed up tests. +// shutdownPollIntervalMax is the max polling interval when checking +// quiescence during Server.Shutdown. Polling starts with a small +// interval and backs off to the max. // Ideally we could find a solution that doesn't involve polling, // but which also doesn't have a high runtime cost (and doesn't // involve any contentious mutexes), but that is left as an // exercise for the reader. -var shutdownPollInterval = 500 * time.Millisecond +const shutdownPollIntervalMax = 500 * time.Millisecond // Shutdown gracefully shuts down the server without interrupting any // active connections. Shutdown works by first closing all open @@ -2731,8 +2732,20 @@ func (srv *Server) Shutdown(ctx context.Context) error { } srv.mu.Unlock() - ticker := time.NewTicker(shutdownPollInterval) - defer ticker.Stop() + pollIntervalBase := time.Millisecond + nextPollInterval := func() time.Duration { + // Add 10% jitter. + interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase/10))) + // Double and clamp for next time. + pollIntervalBase *= 2 + if pollIntervalBase > shutdownPollIntervalMax { + pollIntervalBase = shutdownPollIntervalMax + } + return interval + } + + timer := time.NewTimer(nextPollInterval()) + defer timer.Stop() for { if srv.closeIdleConns() && srv.numListeners() == 0 { return lnerr @@ -2740,7 +2753,8 @@ func (srv *Server) Shutdown(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() - case <-ticker.C: + case <-timer.C: + timer.Reset(nextPollInterval()) } } } |