net: attempt to deflake TestListenCloseListen

Also make it flakier in longtest mode by burning through more
ephemeral ports. (Burning through the ports raised the failure rate
for me locally enough to reliably reproduce the failure in #65175 with
-count=10.)

Fixes #65175 (I hope).

Change-Id: I5f5b68b6bf6a6aa92e66f0288078817041656a3e
Reviewed-on: https://go-review.googlesource.com/c/go/+/557177
Reviewed-by: Damien Neil <dneil@google.com>
Auto-Submit: Bryan Mills <bcmills@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Bryan C. Mills 2024-01-19 19:05:53 -05:00 committed by Gopher Robot
parent 40fab294de
commit 41c05ea4b9
1 changed files with 66 additions and 20 deletions

View File

@ -11,6 +11,7 @@ import (
"net/internal/socktest"
"os"
"runtime"
"sync"
"testing"
"time"
)
@ -294,27 +295,72 @@ func TestPacketConnClose(t *testing.T) {
}
func TestListenCloseListen(t *testing.T) {
const maxTries = 10
for tries := 0; tries < maxTries; tries++ {
ln := newLocalListener(t, "tcp")
addr := ln.Addr().String()
// TODO: This is racy. The selected address could be reused in between this
// Close and the subsequent Listen.
if err := ln.Close(); err != nil {
if perr := parseCloseError(err, false); perr != nil {
t.Error(perr)
}
t.Fatal(err)
}
ln, err := Listen("tcp", addr)
if err == nil {
// Success. (This test didn't always make it here earlier.)
ln.Close()
return
}
t.Errorf("failed on try %d/%d: %v", tries+1, maxTries, err)
if testing.Short() {
t.Parallel()
}
t.Fatalf("failed to listen/close/listen on same address after %d tries", maxTries)
ln := newLocalListener(t, "tcp")
addr := ln.Addr().String()
var wg sync.WaitGroup
wg.Add(1)
go func() {
for {
c, err := ln.Accept()
if err != nil {
wg.Done()
return
}
wg.Add(1)
go func() {
io.Copy(io.Discard, c)
c.Close()
wg.Done()
}()
}
}()
defer wg.Wait()
// Keep a connection alive while we close the listener to try to discourage
// the kernel from reusing the listener's port for some other process.
//
// TODO(bcmills): This empirically seems to work, and we also rely on it in
// TestDialClosedPortFailFast, but I can't find a reference documenting this
// port-reuse behavior.
c, err := Dial("tcp", addr)
defer c.Close()
if err := ln.Close(); err != nil {
if perr := parseCloseError(err, false); perr != nil {
t.Error(perr)
}
t.Fatal(err)
}
if !testing.Short() {
// Burn through some ephemeral ports (without actually accepting any
// connections on them) to try to encourage the kernel to reuse the address
// if it is going to.
lns := make(chan []Listener, 1)
lns <- nil
for i := 0; i < 4000; i++ {
ln := newLocalListener(t, "tcp")
lns <- append(<-lns, ln)
}
defer func() {
for _, ln := range <-lns {
ln.Close()
}
}()
}
ln, err = Listen("tcp", addr)
if err == nil {
// Success. (This test didn't always make it here earlier.)
ln.Close()
return
}
t.Fatalf("failed to listen/close/listen on same address")
}
// See golang.org/issue/6163, golang.org/issue/6987.