mirror of
https://github.com/golang/go
synced 2024-11-23 17:30:02 -07:00
net: prevent spurious on-connect events via epoll on linux
On Linux, adding a socket descriptor to epoll instance before getting the EINPROGRESS return value from connect system call could be a root cause of spurious on-connect events. See golang.org/issue/8276, golang.org/issue/8426 for further information. All credit to Jason Eggleston <jason@eggnet.com> Fixes #8276. Fixes #8426. LGTM=dvyukov R=dvyukov, golang-codereviews, adg, dave, iant, alex.brainman CC=golang-codereviews https://golang.org/cl/120820043
This commit is contained in:
parent
9333fe8e92
commit
c0325f5083
@ -68,16 +68,19 @@ func (fd *netFD) name() string {
|
||||
return fd.net + ":" + ls + "->" + rs
|
||||
}
|
||||
|
||||
func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
|
||||
func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
|
||||
// Do not need to call fd.writeLock here,
|
||||
// because fd is not yet accessible to user,
|
||||
// so no concurrent operations are possible.
|
||||
if err := fd.pd.PrepareWrite(); err != nil {
|
||||
return err
|
||||
}
|
||||
switch err := syscall.Connect(fd.sysfd, ra); err {
|
||||
case syscall.EINPROGRESS, syscall.EALREADY, syscall.EINTR:
|
||||
case nil, syscall.EISCONN:
|
||||
if !deadline.IsZero() && deadline.Before(time.Now()) {
|
||||
return errTimeout
|
||||
}
|
||||
if err := fd.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
case syscall.EINVAL:
|
||||
// On Solaris we can see EINVAL if the socket has
|
||||
@ -92,6 +95,13 @@ func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
|
||||
default:
|
||||
return err
|
||||
}
|
||||
if err := fd.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !deadline.IsZero() {
|
||||
fd.setWriteDeadline(deadline)
|
||||
defer fd.setWriteDeadline(noDeadline)
|
||||
}
|
||||
for {
|
||||
// Performing multiple connect system calls on a
|
||||
// non-blocking socket under Unix variants does not
|
||||
|
@ -313,10 +313,17 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
|
||||
runtime.SetFinalizer(fd, (*netFD).Close)
|
||||
}
|
||||
|
||||
func (fd *netFD) connect(la, ra syscall.Sockaddr) error {
|
||||
func (fd *netFD) connect(la, ra syscall.Sockaddr, deadline time.Time) error {
|
||||
// Do not need to call fd.writeLock here,
|
||||
// because fd is not yet accessible to user,
|
||||
// so no concurrent operations are possible.
|
||||
if err := fd.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
if !deadline.IsZero() {
|
||||
fd.setWriteDeadline(deadline)
|
||||
defer fd.setWriteDeadline(noDeadline)
|
||||
}
|
||||
if !canUseConnectEx(fd.net) {
|
||||
return syscall.Connect(fd.sysfd, ra)
|
||||
}
|
||||
|
@ -107,24 +107,18 @@ func (fd *netFD) dial(laddr, raddr sockaddr, deadline time.Time, toAddr func(sys
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := fd.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
var rsa syscall.Sockaddr
|
||||
if raddr != nil {
|
||||
if rsa, err = raddr.sockaddr(fd.family); err != nil {
|
||||
return err
|
||||
} else if rsa != nil {
|
||||
if !deadline.IsZero() {
|
||||
fd.setWriteDeadline(deadline)
|
||||
}
|
||||
if err := fd.connect(lsa, rsa); err != nil {
|
||||
return err
|
||||
}
|
||||
fd.isConnected = true
|
||||
if !deadline.IsZero() {
|
||||
fd.setWriteDeadline(noDeadline)
|
||||
}
|
||||
}
|
||||
if err := fd.connect(lsa, rsa, deadline); err != nil {
|
||||
return err
|
||||
}
|
||||
fd.isConnected = true
|
||||
} else {
|
||||
if err := fd.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
lsa, _ = syscall.Getsockname(fd.sysfd)
|
||||
|
Loading…
Reference in New Issue
Block a user