mirror of
https://github.com/golang/go
synced 2024-11-26 00:57:56 -07:00
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
This commit is contained in:
parent
f7910128e7
commit
37feacf623
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user