net: use F_DUPFD_CLOEXEC when duping fds

This means that in the common case (modern kernel), we only
make 1 system call to dup instead of two, and we also avoid
grabbing the syscall.ForkLock.

R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/12476043
diff --git a/src/pkg/net/fd_unix.go b/src/pkg/net/fd_unix.go
index 5f8a670..feced2f 100644
--- a/src/pkg/net/fd_unix.go
+++ b/src/pkg/net/fd_unix.go
@@ -11,6 +11,7 @@
 	"os"
 	"runtime"
 	"sync"
+	"sync/atomic"
 	"syscall"
 	"time"
 )
@@ -405,15 +406,46 @@
 	return netfd, nil
 }
 
-func (fd *netFD) dup() (f *os.File, err error) {
+// tryDupCloexec indicates whether F_DUPFD_CLOEXEC should be used.
+// If the kernel doesn't support it, this is set to 0.
+var tryDupCloexec = int32(1)
+
+func dupCloseOnExec(fd int) (newfd int, err error) {
+	if atomic.LoadInt32(&tryDupCloexec) == 1 {
+		r0, _, e1 := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0)
+		switch e1 {
+		case 0:
+			return int(r0), nil
+		case syscall.EINVAL:
+			// Old kernel. Fall back to the portable way
+			// from now on.
+			atomic.StoreInt32(&tryDupCloexec, 0)
+		default:
+			return -1, e1
+		}
+	}
+	return dupCloseOnExecOld(fd)
+}
+
+// dupCloseOnExecUnixOld is the traditional way to dup an fd and
+// set its O_CLOEXEC bit, using two system calls.
+func dupCloseOnExecOld(fd int) (newfd int, err error) {
 	syscall.ForkLock.RLock()
-	ns, err := syscall.Dup(fd.sysfd)
+	defer syscall.ForkLock.RUnlock()
+	newfd, err = syscall.Dup(fd)
+	if err != nil {
+		return -1, err
+	}
+	syscall.CloseOnExec(newfd)
+	return
+}
+
+func (fd *netFD) dup() (f *os.File, err error) {
+	ns, err := dupCloseOnExec(fd.sysfd)
 	if err != nil {
 		syscall.ForkLock.RUnlock()
 		return nil, &OpError{"dup", fd.net, fd.laddr, err}
 	}
-	syscall.CloseOnExec(ns)
-	syscall.ForkLock.RUnlock()
 
 	// We want blocking mode for the new fd, hence the double negative.
 	// This also puts the old fd into blocking mode, meaning that
diff --git a/src/pkg/net/file_unix.go b/src/pkg/net/file_unix.go
index 4c8403e..1e7420c 100644
--- a/src/pkg/net/file_unix.go
+++ b/src/pkg/net/file_unix.go
@@ -12,14 +12,11 @@
 )
 
 func newFileFD(f *os.File) (*netFD, error) {
-	syscall.ForkLock.RLock()
-	fd, err := syscall.Dup(int(f.Fd()))
+	fd, err := dupCloseOnExec(int(f.Fd()))
 	if err != nil {
-		syscall.ForkLock.RUnlock()
 		return nil, os.NewSyscallError("dup", err)
 	}
-	syscall.CloseOnExec(fd)
-	syscall.ForkLock.RUnlock()
+
 	if err = syscall.SetNonblock(fd, true); err != nil {
 		closesocket(fd)
 		return nil, err
diff --git a/src/pkg/syscall/zerrors_freebsd_386.go b/src/pkg/syscall/zerrors_freebsd_386.go
index 24af6ab..43d7c59 100644
--- a/src/pkg/syscall/zerrors_freebsd_386.go
+++ b/src/pkg/syscall/zerrors_freebsd_386.go
@@ -438,7 +438,9 @@
 	FLUSHO                            = 0x800000
 	F_CANCEL                          = 0x5
 	F_DUP2FD                          = 0xa
+	F_DUP2FD_CLOEXEC                  = 0x12
 	F_DUPFD                           = 0x0
+	F_DUPFD_CLOEXEC                   = 0x11
 	F_GETFD                           = 0x1
 	F_GETFL                           = 0x3
 	F_GETLK                           = 0xb
diff --git a/src/pkg/syscall/zerrors_freebsd_amd64.go b/src/pkg/syscall/zerrors_freebsd_amd64.go
index d766cd1..8e03f45 100644
--- a/src/pkg/syscall/zerrors_freebsd_amd64.go
+++ b/src/pkg/syscall/zerrors_freebsd_amd64.go
@@ -438,7 +438,9 @@
 	FLUSHO                            = 0x800000
 	F_CANCEL                          = 0x5
 	F_DUP2FD                          = 0xa
+	F_DUP2FD_CLOEXEC                  = 0x12
 	F_DUPFD                           = 0x0
+	F_DUPFD_CLOEXEC                   = 0x11
 	F_GETFD                           = 0x1
 	F_GETFL                           = 0x3
 	F_GETLK                           = 0xb