}
func BenchmarkGoLookupIP(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
goLookupIP("www.example.com")
}
}
func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
goLookupIP("some.nonexistent")
}
}
func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
onceLoadConfig.Do(loadDefaultConfig)
if cfg.dnserr != nil || cfg.dnsConfig == nil {
b.Fatalf("loadConfig failed: %v", cfg.dnserr)
}
func BenchmarkDNSNames(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
benchmarks := append(tests, []testCase{
{strings.Repeat("a", 63), true},
{strings.Repeat("a", 64), false},
// Do not need to call fd.writeLock here,
// because fd is not yet accessible to user,
// so no concurrent operations are possible.
- switch err := syscall.Connect(fd.sysfd, ra); err {
+ switch err := connectFunc(fd.sysfd, ra); err {
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
case nil, syscall.EISCONN:
if !deadline.IsZero() && deadline.Before(time.Now()) {
if err := fd.pd.WaitWrite(); err != nil {
return err
}
- nerr, err := syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
+ nerr, err := getsockoptIntFunc(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
if err != nil {
return err
}
func (fd *netFD) destroy() {
// Poller may want to unregister fd in readiness notification mechanism,
- // so this must be executed before closesocket.
+ // so this must be executed before closeFunc.
fd.pd.Close()
- closesocket(fd.sysfd)
+ closeFunc(fd.sysfd)
fd.sysfd = -1
runtime.SetFinalizer(fd, nil)
}
}
if netfd, err = newFD(s, fd.family, fd.sotype, fd.net); err != nil {
- closesocket(s)
+ closeFunc(s)
return nil, err
}
if err = netfd.init(); err != nil {
return os.NewFile(uintptr(ns), fd.name()), nil
}
-
-func closesocket(s int) error {
- return syscall.Close(s)
-}
}
}
-func closesocket(s syscall.Handle) error {
- return syscall.Closesocket(s)
-}
-
func canUseConnectEx(net string) bool {
switch net {
case "udp", "udp4", "udp6", "ip", "ip4", "ip6":
defer fd.setWriteDeadline(noDeadline)
}
if !canUseConnectEx(fd.net) {
- return syscall.Connect(fd.sysfd, ra)
+ return connectFunc(fd.sysfd, ra)
}
// ConnectEx windows API requires an unconnected, previously bound socket.
if la == nil {
o := &fd.wop
o.sa = ra
_, err := wsrv.ExecIO(o, "ConnectEx", func(o *operation) error {
- return syscall.ConnectEx(o.fd.sysfd, o.sa, nil, 0, nil, &o.o)
+ return connectExFunc(o.fd.sysfd, o.sa, nil, 0, nil, &o.o)
})
if err != nil {
return err
return
}
// Poller may want to unregister fd in readiness notification mechanism,
- // so this must be executed before closesocket.
+ // so this must be executed before closeFunc.
fd.pd.Close()
- closesocket(fd.sysfd)
+ closeFunc(fd.sysfd)
fd.sysfd = syscall.InvalidHandle
// no need for a finalizer anymore
runtime.SetFinalizer(fd, nil)
// Associate our new socket with IOCP.
netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
if err != nil {
- closesocket(s)
+ closeFunc(s)
return nil, &OpError{"accept", fd.net, fd.laddr, err}
}
if err := netfd.init(); err != nil {
}
if err = syscall.SetNonblock(fd, true); err != nil {
- closesocket(fd)
+ closeFunc(fd)
return nil, err
}
sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE)
if err != nil {
- closesocket(fd)
+ closeFunc(fd)
return nil, os.NewSyscallError("getsockopt", err)
}
lsa, _ := syscall.Getsockname(fd)
switch lsa.(type) {
default:
- closesocket(fd)
+ closeFunc(fd)
return nil, syscall.EINVAL
case *syscall.SockaddrInet4:
family = syscall.AF_INET
netfd, err := newFD(fd, family, sotype, laddr.Network())
if err != nil {
- closesocket(fd)
+ closeFunc(fd)
return nil, err
}
if err := netfd.init(); err != nil {
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build freebsd linux
+
+package net
+
+import "syscall"
+
+var (
+ // Placeholders for socket system calls.
+ accept4Func func(int, int) (int, syscall.Sockaddr, error) = syscall.Accept4
+)
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package net
+
+import "syscall"
+
+var (
+ // Placeholders for socket system calls.
+ socketFunc func(int, int, int) (int, error) = syscall.Socket
+ closeFunc func(int) error = syscall.Close
+ connectFunc func(int, syscall.Sockaddr) error = syscall.Connect
+ acceptFunc func(int) (int, syscall.Sockaddr, error) = syscall.Accept
+ getsockoptIntFunc func(int, int, int) (int, error) = syscall.GetsockoptInt
+)
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import "syscall"
+
+var (
+ // Placeholders for socket system calls.
+ socketFunc func(int, int, int) (syscall.Handle, error) = syscall.Socket
+ closeFunc func(syscall.Handle) error = syscall.Closesocket
+ connectFunc func(syscall.Handle, syscall.Sockaddr) error = syscall.Connect
+ connectExFunc func(syscall.Handle, syscall.Sockaddr, *byte, uint32, *uint32, *syscall.Overlapped) error = syscall.ConnectEx
+)
}
func BenchmarkInterfaces(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
if _, err := Interfaces(); err != nil {
b.Fatal(err)
}
func BenchmarkInterfaceByIndex(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
ifi := loopbackInterface()
if ifi == nil {
b.Skip("loopback interface not found")
}
func BenchmarkInterfaceByName(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
ifi := loopbackInterface()
if ifi == nil {
b.Skip("loopback interface not found")
}
func BenchmarkInterfaceAddrs(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
if _, err := InterfaceAddrs(); err != nil {
b.Fatal(err)
}
func BenchmarkInterfacesAndAddrs(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
ifi := loopbackInterface()
if ifi == nil {
b.Skip("loopback interface not found")
}
func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
ifi := loopbackInterface()
if ifi == nil {
b.Skip("loopback interface not found")
}
func BenchmarkParseIP(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
for _, tt := range parseIPTests {
ParseIP(tt.in)
}
func BenchmarkIPString(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
for _, tt := range ipStringTests {
if tt.in != nil {
}
func BenchmarkIPMaskString(b *testing.B) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
for i := 0; i < b.N; i++ {
for _, tt := range ipMaskStringTests {
tt.in.String()
)
func probeIPv4Stack() bool {
- s, err := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
switch err {
case syscall.EAFNOSUPPORT, syscall.EPROTONOSUPPORT:
return false
case nil:
- closesocket(s)
+ closeFunc(s)
}
return true
}
}
for i := range probes {
- s, err := syscall.Socket(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+ s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
if err != nil {
continue
}
- defer closesocket(s)
+ defer closeFunc(s)
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, probes[i].value)
sa, err := probes[i].laddr.sockaddr(syscall.AF_INET6)
if err != nil {
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build freebsd linux
+
+package net
+
+func init() {
+ extraTestHookInstallers = append(extraTestHookInstallers, installAccept4TestHook)
+ extraTestHookUninstallers = append(extraTestHookUninstallers, uninstallAccept4TestHook)
+}
+
+var (
+ // Placeholders for saving original socket system calls.
+ origAccept4 = accept4Func
+)
+
+func installAccept4TestHook() {
+ accept4Func = sw.Accept4
+}
+
+func uninstallAccept4TestHook() {
+ accept4Func = origAccept4
+}
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+func installTestHooks() {}
+
+func uninstallTestHooks() {}
+
+func forceCloseSockets() {}
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+import (
+ "fmt"
+ "net/internal/socktest"
+ "os"
+ "runtime"
+ "sort"
+ "strings"
+ "testing"
+)
+
+var sw socktest.Switch
+
+func TestMain(m *testing.M) {
+ installTestHooks()
+
+ st := m.Run()
+
+ if !testing.Short() {
+ printLeakedGoroutines()
+ printLeakedSockets()
+ printSocketStats()
+ }
+ forceCloseSockets()
+ uninstallTestHooks()
+ os.Exit(st)
+}
+
+func printLeakedGoroutines() {
+ gss := leakedGoroutines()
+ if len(gss) == 0 {
+ return
+ }
+ fmt.Fprintf(os.Stderr, "Leaked goroutines:\n")
+ for _, gs := range gss {
+ fmt.Fprintf(os.Stderr, "%v\n", gs)
+ }
+ fmt.Fprintf(os.Stderr, "\n")
+}
+
+// leakedGoroutines returns a list of remaining goroutins used in test
+// cases.
+func leakedGoroutines() []string {
+ var gss []string
+ b := make([]byte, 2<<20)
+ b = b[:runtime.Stack(b, true)]
+ for _, s := range strings.Split(string(b), "\n\n") {
+ ss := strings.SplitN(s, "\n", 2)
+ if len(ss) != 2 {
+ continue
+ }
+ stack := strings.TrimSpace(ss[1])
+ if !strings.Contains(stack, "created by net") {
+ continue
+ }
+ gss = append(gss, stack)
+ }
+ sort.Strings(gss)
+ return gss
+}
+
+func printLeakedSockets() {
+ sos := sw.Sockets()
+ if len(sos) == 0 {
+ return
+ }
+ fmt.Fprintf(os.Stderr, "Leaked sockets:\n")
+ for s, so := range sos {
+ fmt.Fprintf(os.Stderr, "%v: %+v\n", s, so)
+ }
+ fmt.Fprintf(os.Stderr, "\n")
+}
+
+func printSocketStats() {
+ sts := sw.Stats()
+ if len(sts) == 0 {
+ return
+ }
+ fmt.Fprintf(os.Stderr, "Socket statistical information:\n")
+ for _, st := range sts {
+ fmt.Fprintf(os.Stderr, "%+v\n", st)
+ }
+ fmt.Fprintf(os.Stderr, "\n")
+}
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
+
+package net
+
+var (
+ // Placeholders for saving original socket system calls.
+ origSocket = socketFunc
+ origClose = closeFunc
+ origConnect = connectFunc
+ origAccept = acceptFunc
+ origGetsockoptInt = getsockoptIntFunc
+
+ extraTestHookInstallers []func()
+ extraTestHookUninstallers []func()
+)
+
+func installTestHooks() {
+ socketFunc = sw.Socket
+ closeFunc = sw.Close
+ connectFunc = sw.Connect
+ acceptFunc = sw.Accept
+ getsockoptIntFunc = sw.GetsockoptInt
+
+ for _, fn := range extraTestHookInstallers {
+ fn()
+ }
+}
+
+func uninstallTestHooks() {
+ socketFunc = origSocket
+ closeFunc = origClose
+ connectFunc = origConnect
+ acceptFunc = origAccept
+ getsockoptIntFunc = origGetsockoptInt
+
+ for _, fn := range extraTestHookUninstallers {
+ fn()
+ }
+}
+
+func forceCloseSockets() {
+ for s := range sw.Sockets() {
+ closeFunc(s)
+ }
+}
--- /dev/null
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package net
+
+var (
+ // Placeholders for saving original socket system calls.
+ origSocket = socketFunc
+ origClosesocket = closeFunc
+ origConnect = connectFunc
+ origConnectEx = connectExFunc
+)
+
+func installTestHooks() {
+ socketFunc = sw.Socket
+ closeFunc = sw.Closesocket
+ connectFunc = sw.Connect
+ connectExFunc = sw.ConnectEx
+}
+
+func uninstallTestHooks() {
+ socketFunc = origSocket
+ closeFunc = origClosesocket
+ connectFunc = origConnect
+ connectExFunc = origConnectEx
+}
+
+func forceCloseSockets() {
+ for s := range sw.Sockets() {
+ closeFunc(s)
+ }
+}
// Wrapper around the socket system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
func sysSocket(family, sotype, proto int) (int, error) {
- s, err := syscall.Socket(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
+ s, err := socketFunc(family, sotype|syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC, proto)
// On Linux the SOCK_NONBLOCK and SOCK_CLOEXEC flags were
// introduced in 2.6.27 kernel and on FreeBSD both flags were
// introduced in 10 kernel. If we get an EINVAL error on Linux
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
- s, err = syscall.Socket(family, sotype, proto)
+ s, err = socketFunc(family, sotype, proto)
if err == nil {
syscall.CloseOnExec(s)
}
return -1, err
}
if err = syscall.SetNonblock(s, true); err != nil {
- syscall.Close(s)
+ closeFunc(s)
return -1, err
}
return s, nil
// Wrapper around the accept system call that marks the returned file
// descriptor as nonblocking and close-on-exec.
func accept(s int) (int, syscall.Sockaddr, error) {
- ns, sa, err := syscall.Accept4(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
+ ns, sa, err := accept4Func(s, syscall.SOCK_NONBLOCK|syscall.SOCK_CLOEXEC)
// On Linux the accept4 system call was introduced in 2.6.28
// kernel and on FreeBSD it was introduced in 10 kernel. If we
// get an ENOSYS error on both Linux and FreeBSD, or EINVAL
// because we have put fd.sysfd into non-blocking mode.
// However, a call to the File method will put it back into
// blocking mode. We can't take that risk, so no use of ForkLock here.
- ns, sa, err = syscall.Accept(s)
+ ns, sa, err = acceptFunc(s)
if err == nil {
syscall.CloseOnExec(ns)
}
return -1, nil, err
}
if err = syscall.SetNonblock(ns, true); err != nil {
- syscall.Close(ns)
+ closeFunc(ns)
return -1, nil, err
}
return ns, sa, nil
return nil, err
}
if err = setDefaultSockopts(s, family, sotype, ipv6only); err != nil {
- closesocket(s)
+ closeFunc(s)
return nil, err
}
if fd, err = newFD(s, family, sotype, net); err != nil {
- closesocket(s)
+ closeFunc(s)
return nil, err
}
return syscall.SOMAXCONN
}
-func sysSocket(f, t, p int) (syscall.Handle, error) {
+func sysSocket(family, sotype, proto int) (syscall.Handle, error) {
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
- s, err := syscall.Socket(f, t, p)
+ s, err := socketFunc(family, sotype, proto)
if err == nil {
syscall.CloseOnExec(s)
}
func sysSocket(family, sotype, proto int) (int, error) {
// See ../syscall/exec_unix.go for description of ForkLock.
syscall.ForkLock.RLock()
- s, err := syscall.Socket(family, sotype, proto)
+ s, err := socketFunc(family, sotype, proto)
if err == nil {
syscall.CloseOnExec(s)
}
return -1, err
}
if err = syscall.SetNonblock(s, true); err != nil {
- syscall.Close(s)
+ closeFunc(s)
return -1, err
}
return s, nil
// because we have put fd.sysfd into non-blocking mode.
// However, a call to the File method will put it back into
// blocking mode. We can't take that risk, so no use of ForkLock here.
- ns, sa, err := syscall.Accept(s)
+ ns, sa, err := acceptFunc(s)
if err == nil {
syscall.CloseOnExec(ns)
}
return -1, nil, err
}
if err = syscall.SetNonblock(ns, true); err != nil {
- syscall.Close(ns)
+ closeFunc(ns)
return -1, nil, err
}
return ns, sa, nil
}
func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
+ uninstallTestHooks()
+ defer installTestHooks()
+
const msgLen = 512
conns := b.N
numConcurrent := runtime.GOMAXPROCS(-1) * 2
// The benchmark stresses concurrent reading and writing to the same connection.
// Such pattern is used in net/http and net/rpc.
+ uninstallTestHooks()
+ defer installTestHooks()
b.StopTimer()
P := runtime.GOMAXPROCS(0)