aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/net/http/http_test.go5
-rw-r--r--src/net/http/server.go28
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())
}
}
}