diff options
author | Paul Marks <pmarks@google.com> | 2016-04-04 14:13:56 -0700 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@golang.org> | 2016-04-05 05:12:22 +0000 |
commit | 869e576517f825aecdc8730b0d22f8d6b59bd749 (patch) | |
tree | 32f9c972642b9e37c8c78ea04d44cdc746ed2a12 /src/net/dial_test.go | |
parent | bbbd572c10e8e28d343a559b9c0ceef9074c719c (diff) | |
download | go-869e576517f825aecdc8730b0d22f8d6b59bd749.tar.gz go-869e576517f825aecdc8730b0d22f8d6b59bd749.zip |
net: wait for cancelation goroutine before returning from connect.
This fixes a race which made it possible to cancel a connection after
returning from net.Dial.
Fixes #15035
Fixes #15078
Change-Id: Iec6215009538362f7ad9f408a33549f3e94d1606
Reviewed-on: https://go-review.googlesource.com/21497
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/net/dial_test.go')
-rw-r--r-- | src/net/dial_test.go | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/src/net/dial_test.go b/src/net/dial_test.go index 04e0fdae44..2fc75c6356 100644 --- a/src/net/dial_test.go +++ b/src/net/dial_test.go @@ -5,6 +5,7 @@ package net import ( + "bufio" "internal/testenv" "io" "net/internal/socktest" @@ -871,3 +872,84 @@ func TestDialCancel(t *testing.T) { } } } + +func TestCancelAfterDial(t *testing.T) { + if testing.Short() { + t.Skip("avoiding time.Sleep") + } + + ln, err := newLocalListener("tcp") + if err != nil { + t.Fatal(err) + } + + var wg sync.WaitGroup + wg.Add(1) + defer func() { + ln.Close() + wg.Wait() + }() + + // Echo back the first line of each incoming connection. + go func() { + for { + c, err := ln.Accept() + if err != nil { + break + } + rb := bufio.NewReader(c) + line, err := rb.ReadString('\n') + if err != nil { + t.Error(err) + c.Close() + continue + } + if _, err := c.Write([]byte(line)); err != nil { + t.Error(err) + } + c.Close() + } + wg.Done() + }() + + try := func() { + cancel := make(chan struct{}) + d := &Dialer{Cancel: cancel} + c, err := d.Dial("tcp", ln.Addr().String()) + + // Immediately after dialing, request cancelation and sleep. + // Before Issue 15078 was fixed, this would cause subsequent operations + // to fail with an i/o timeout roughly 50% of the time. + close(cancel) + time.Sleep(10 * time.Millisecond) + + if err != nil { + t.Fatal(err) + } + defer c.Close() + + // Send some data to confirm that the connection is still alive. + const message = "echo!\n" + if _, err := c.Write([]byte(message)); err != nil { + t.Fatal(err) + } + + // The server should echo the line, and close the connection. + rb := bufio.NewReader(c) + line, err := rb.ReadString('\n') + if err != nil { + t.Fatal(err) + } + if line != message { + t.Errorf("got %q; want %q", line, message) + } + if _, err := rb.ReadByte(); err != io.EOF { + t.Errorf("got %v; want %v", err, io.EOF) + } + } + + // This bug manifested about 50% of the time, so try it a few times. + for i := 0; i < 10; i++ { + try() + } +} |