]> Cypherpunks repositories - gostls13.git/commitdiff
net: use F_DUPFD_CLOEXEC when duping fds
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 5 Aug 2013 22:43:45 +0000 (15:43 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 5 Aug 2013 22:43:45 +0000 (15:43 -0700)
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

src/pkg/net/fd_unix.go
src/pkg/net/file_unix.go
src/pkg/syscall/zerrors_freebsd_386.go
src/pkg/syscall/zerrors_freebsd_amd64.go

index 5f8a6705df5f7fa0046c2eeb6e1909a8a1b1a0f5..feced2f76132e65d81d743f1d83fd1cbff75df0e 100644 (file)
@@ -11,6 +11,7 @@ import (
        "os"
        "runtime"
        "sync"
+       "sync/atomic"
        "syscall"
        "time"
 )
@@ -405,15 +406,46 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (netfd *netFD, err e
        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
index 4c8403e40631ad89750b0c27361d877ffd958451..1e7420cf7745ce33520235c02b10b5aeb64fb066 100644 (file)
@@ -12,14 +12,11 @@ import (
 )
 
 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
index 24af6ab09b8712e602baa50dab58c739a3adde0e..43d7c5969d99d2c419bf46f3955b1f766ce31ba5 100644 (file)
@@ -438,7 +438,9 @@ const (
        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
index d766cd13a08d4c6ed094accdf4139b14de6af1a3..8e03f45e2f8c85b4af19de2f1166bb00eb4978eb 100644 (file)
@@ -438,7 +438,9 @@ const (
        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