net: compute the Dialer deadline exactly once.
When dialing with a relative Timeout instead of an absolute Deadline,
the deadline function only makes sense if called before doing any
time-consuming work.
This change calls deadline exactly once, storing the result until the
Dial operation completes. The partialDeadline implementation is
reverted to the following patch set 3:
https://go-review.googlesource.com/#/c/8768/3..4/src/net/dial.go
Otherwise, when dialing a name with multiple IP addresses, or when DNS
is slow, the recomputed deadline causes the total Timeout to exceed that
requested by the user.
Fixes #11796
Change-Id: I5e1f0d545f9e86a4e0e2ac31a9bd108849cf0fdf
Reviewed-on: https://go-review.googlesource.com/12442
Run-TryBot: Paul Marks <pmarks@google.com>
Run-TryBot: Mikio Hara <mikioh.mikioh@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/src/net/dial_test.go b/src/net/dial_test.go
index 9848f30..cfd7e09 100644
--- a/src/net/dial_test.go
+++ b/src/net/dial_test.go
@@ -228,10 +228,19 @@
return c, err
}
-func dialClosedPort() time.Duration {
+func dialClosedPort() (actual, expected time.Duration) {
+ // Estimate the expected time for this platform.
+ // On Windows, dialing a closed port takes roughly 1 second,
+ // but other platforms should be instantaneous.
+ if runtime.GOOS == "windows" {
+ expected = 1095 * time.Millisecond
+ } else {
+ expected = 95 * time.Millisecond
+ }
+
l, err := Listen("tcp", "127.0.0.1:0")
if err != nil {
- return 999 * time.Hour
+ return 999 * time.Hour, expected
}
addr := l.Addr().String()
l.Close()
@@ -246,7 +255,7 @@
}
elapsed := time.Now().Sub(startTime)
if i == 2 {
- return elapsed
+ return elapsed, expected
}
}
}
@@ -259,16 +268,7 @@
t.Skip("both IPv4 and IPv6 are required")
}
- // Determine the time required to dial a closed port.
- // On Windows, this takes roughly 1 second, but other platforms
- // are expected to be instantaneous.
- closedPortDelay := dialClosedPort()
- var expectClosedPortDelay time.Duration
- if runtime.GOOS == "windows" {
- expectClosedPortDelay = 1095 * time.Millisecond
- } else {
- expectClosedPortDelay = 95 * time.Millisecond
- }
+ closedPortDelay, expectClosedPortDelay := dialClosedPort()
if closedPortDelay > expectClosedPortDelay {
t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
}
@@ -551,8 +551,7 @@
{now.Add(1 * time.Millisecond), now, 1, noDeadline, errTimeout},
}
for i, tt := range testCases {
- d := Dialer{Deadline: tt.deadline}
- deadline, err := d.partialDeadline(tt.now, tt.addrs)
+ deadline, err := partialDeadline(tt.now, tt.deadline, tt.addrs)
if err != tt.expectErr {
t.Errorf("#%d: got %v; want %v", i, err, tt.expectErr)
}
@@ -605,6 +604,11 @@
t.Skip("both IPv4 and IPv6 are required")
}
+ closedPortDelay, expectClosedPortDelay := dialClosedPort()
+ if closedPortDelay > expectClosedPortDelay {
+ t.Errorf("got %v; want <= %v", closedPortDelay, expectClosedPortDelay)
+ }
+
origTestHookLookupIP := testHookLookupIP
defer func() { testHookLookupIP = origTestHookLookupIP }()
testHookLookupIP = lookupLocalhost
@@ -618,7 +622,7 @@
}
}
- const T = 100 * time.Millisecond
+ var timeout = 100*time.Millisecond + closedPortDelay
for _, dualstack := range []bool{false, true} {
dss, err := newDualStackServer([]streamListener{
{network: "tcp4", address: "127.0.0.1"},
@@ -632,7 +636,7 @@
t.Fatal(err)
}
- d := &Dialer{DualStack: dualstack, Timeout: T}
+ d := &Dialer{DualStack: dualstack, Timeout: timeout}
for range dss.lns {
c, err := d.Dial("tcp", JoinHostPort("localhost", dss.port))
if err != nil {
@@ -648,7 +652,7 @@
c.Close()
}
}
- time.Sleep(2 * T) // wait for the dial racers to stop
+ time.Sleep(timeout * 3 / 2) // wait for the dial racers to stop
}
func TestDialerKeepAlive(t *testing.T) {