net: fix tester goroutine leakage in tests

This change tries to stop various tester goroutines at the end of each
scope for avoiding interference between test cases including benchmarks.
Not yet finished completely but enough to land upcoming changes to Dial
functions. The rest will be fixed later.

Change-Id: Ic38b8681a3a2ddbcd69ba3696f24a61d418a0346
Reviewed-on: https://go-review.googlesource.com/8398
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index 68ded5d..3a21452 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -4,11 +4,74 @@
 
 package net
 
-import "sync"
+import (
+	"fmt"
+	"os"
+	"sync"
+)
+
+func newLocalListener(network string) (Listener, error) {
+	switch network {
+	case "tcp", "tcp4", "tcp6":
+		if supportsIPv4 {
+			return Listen("tcp4", "127.0.0.1:0")
+		}
+		if supportsIPv6 {
+			return Listen("tcp6", "[::1]:0")
+		}
+	case "unix", "unixpacket":
+		return Listen(network, testUnixAddr())
+	}
+	return nil, fmt.Errorf("%s is not supported", network)
+}
+
+type localServer struct {
+	lnmu sync.RWMutex
+	Listener
+	done chan bool // signal that indicates server stopped
+}
+
+func (ls *localServer) buildup(handler func(*localServer, Listener)) error {
+	go func() {
+		handler(ls, ls.Listener)
+		close(ls.done)
+	}()
+	return nil
+}
+
+func (ls *localServer) teardown() error {
+	ls.lnmu.Lock()
+	if ls.Listener != nil {
+		network := ls.Listener.Addr().Network()
+		address := ls.Listener.Addr().String()
+		ls.Listener.Close()
+		<-ls.done
+		ls.Listener = nil
+		switch network {
+		case "unix", "unixpacket":
+			os.Remove(address)
+		}
+	}
+	ls.lnmu.Unlock()
+	return nil
+}
+
+func newLocalServer(network string) (*localServer, error) {
+	ln, err := newLocalListener(network)
+	if err != nil {
+		return nil, err
+	}
+	return &localServer{Listener: ln, done: make(chan bool)}, nil
+}
 
 type streamListener struct {
-	net, addr string
-	ln        Listener
+	network, address string
+	Listener
+	done chan bool // signal that indicates server stopped
+}
+
+func (sl *streamListener) newLocalServer() (*localServer, error) {
+	return &localServer{Listener: sl.Listener, done: make(chan bool)}, nil
 }
 
 type dualStackServer struct {
@@ -20,9 +83,12 @@
 	cs  []Conn // established connections at the passive open side
 }
 
-func (dss *dualStackServer) buildup(server func(*dualStackServer, Listener)) error {
+func (dss *dualStackServer) buildup(handler func(*dualStackServer, Listener)) error {
 	for i := range dss.lns {
-		go server(dss, dss.lns[i].ln)
+		go func(i int) {
+			handler(dss, dss.lns[i].Listener)
+			close(dss.lns[i].done)
+		}(i)
 	}
 	return nil
 }
@@ -34,12 +100,13 @@
 	return nil
 }
 
-func (dss *dualStackServer) teardownNetwork(net string) error {
+func (dss *dualStackServer) teardownNetwork(network string) error {
 	dss.lnmu.Lock()
 	for i := range dss.lns {
-		if net == dss.lns[i].net && dss.lns[i].ln != nil {
-			dss.lns[i].ln.Close()
-			dss.lns[i].ln = nil
+		if network == dss.lns[i].network && dss.lns[i].Listener != nil {
+			dss.lns[i].Listener.Close()
+			<-dss.lns[i].done
+			dss.lns[i].Listener = nil
 		}
 	}
 	dss.lnmu.Unlock()
@@ -49,15 +116,18 @@
 func (dss *dualStackServer) teardown() error {
 	dss.lnmu.Lock()
 	for i := range dss.lns {
-		if dss.lns[i].ln != nil {
-			dss.lns[i].ln.Close()
+		if dss.lns[i].Listener != nil {
+			dss.lns[i].Listener.Close()
+			<-dss.lns[i].done
 		}
 	}
+	dss.lns = dss.lns[:0]
 	dss.lnmu.Unlock()
 	dss.cmu.Lock()
 	for _, c := range dss.cs {
 		c.Close()
 	}
+	dss.cs = dss.cs[:0]
 	dss.cmu.Unlock()
 	return nil
 }
@@ -65,15 +135,20 @@
 func newDualStackServer(lns []streamListener) (*dualStackServer, error) {
 	dss := &dualStackServer{lns: lns, port: "0"}
 	for i := range dss.lns {
-		ln, err := Listen(dss.lns[i].net, dss.lns[i].addr+":"+dss.port)
+		ln, err := Listen(dss.lns[i].network, JoinHostPort(dss.lns[i].address, dss.port))
 		if err != nil {
-			dss.teardown()
+			for _, ln := range dss.lns {
+				ln.Listener.Close()
+			}
 			return nil, err
 		}
-		dss.lns[i].ln = ln
+		dss.lns[i].Listener = ln
+		dss.lns[i].done = make(chan bool)
 		if dss.port == "0" {
 			if _, dss.port, err = SplitHostPort(ln.Addr().String()); err != nil {
-				dss.teardown()
+				for _, ln := range dss.lns {
+					ln.Listener.Close()
+				}
 				return nil, err
 			}
 		}