1
0
mirror of https://github.com/golang/go synced 2024-11-13 20:30:27 -07:00
go/src/net/fd_windows.go
Ian Lance Taylor bf0f692202 net: handle spurious netpoll wakeups in connect
In some cases the netpoll code can cause a spurious wakeup. This is
normally harmless, as the woken up code simply retries the operation.
However, for connect, the test we were using to see whether the
connect had succeeded (setsockopt(SO_ERROR)) was not reliable in the
case of a spurious wakeup.  Change to using a reliable test (getpeername).
On Darwin we used a different technique: a second call to connect;
change Darwin to use getpeername as well.

Return the result of getpeername to avoid having to call it twice.

Fixes #19289.

Change-Id: I119ec8e7a41f482f1e590d4c65a37f6103fa22d9
Reviewed-on: https://go-review.googlesource.com/45815
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
2017-06-15 22:39:39 +00:00

240 lines
6.0 KiB
Go

// Copyright 2010 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 (
"context"
"internal/poll"
"os"
"runtime"
"syscall"
"unsafe"
)
// canUseConnectEx reports whether we can use the ConnectEx Windows API call
// for the given network type.
func canUseConnectEx(net string) bool {
switch net {
case "tcp", "tcp4", "tcp6":
return true
}
// ConnectEx windows API does not support connectionless sockets.
return false
}
// Network file descriptor.
type netFD struct {
pfd poll.FD
// immutable until Close
family int
sotype int
isConnected bool
net string
laddr Addr
raddr Addr
}
func newFD(sysfd syscall.Handle, family, sotype int, net string) (*netFD, error) {
ret := &netFD{
pfd: poll.FD{
Sysfd: sysfd,
IsStream: sotype == syscall.SOCK_STREAM,
ZeroReadIsEOF: sotype != syscall.SOCK_DGRAM && sotype != syscall.SOCK_RAW,
},
family: family,
sotype: sotype,
net: net,
}
return ret, nil
}
func (fd *netFD) init() error {
errcall, err := fd.pfd.Init(fd.net)
if errcall != "" {
err = wrapSyscallError(errcall, err)
}
return err
}
func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.laddr = laddr
fd.raddr = raddr
runtime.SetFinalizer(fd, (*netFD).Close)
}
// Always returns nil for connected peer address result.
func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (syscall.Sockaddr, 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 nil, err
}
if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
fd.pfd.SetWriteDeadline(deadline)
defer fd.pfd.SetWriteDeadline(noDeadline)
}
if !canUseConnectEx(fd.net) {
err := connectFunc(fd.pfd.Sysfd, ra)
return nil, os.NewSyscallError("connect", err)
}
// ConnectEx windows API requires an unconnected, previously bound socket.
if la == nil {
switch ra.(type) {
case *syscall.SockaddrInet4:
la = &syscall.SockaddrInet4{}
case *syscall.SockaddrInet6:
la = &syscall.SockaddrInet6{}
default:
panic("unexpected type in connect")
}
if err := syscall.Bind(fd.pfd.Sysfd, la); err != nil {
return nil, os.NewSyscallError("bind", err)
}
}
// Wait for the goroutine converting context.Done into a write timeout
// to exist, otherwise our caller might cancel the context and
// cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
done := make(chan bool) // must be unbuffered
defer func() { done <- true }()
go func() {
select {
case <-ctx.Done():
// Force the runtime's poller to immediately give
// up waiting for writability.
fd.pfd.SetWriteDeadline(aLongTimeAgo)
<-done
case <-done:
}
}()
// Call ConnectEx API.
if err := fd.pfd.ConnectEx(ra); err != nil {
select {
case <-ctx.Done():
return nil, mapErr(ctx.Err())
default:
if _, ok := err.(syscall.Errno); ok {
err = os.NewSyscallError("connectex", err)
}
return nil, err
}
}
// Refresh socket properties.
return nil, os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.pfd.Sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.pfd.Sysfd)), int32(unsafe.Sizeof(fd.pfd.Sysfd))))
}
func (fd *netFD) Close() error {
runtime.SetFinalizer(fd, nil)
return fd.pfd.Close()
}
func (fd *netFD) shutdown(how int) error {
err := fd.pfd.Shutdown(how)
runtime.KeepAlive(fd)
return err
}
func (fd *netFD) closeRead() error {
return fd.shutdown(syscall.SHUT_RD)
}
func (fd *netFD) closeWrite() error {
return fd.shutdown(syscall.SHUT_WR)
}
func (fd *netFD) Read(buf []byte) (int, error) {
n, err := fd.pfd.Read(buf)
runtime.KeepAlive(fd)
return n, wrapSyscallError("wsarecv", err)
}
func (fd *netFD) readFrom(buf []byte) (int, syscall.Sockaddr, error) {
n, sa, err := fd.pfd.ReadFrom(buf)
runtime.KeepAlive(fd)
return n, sa, wrapSyscallError("wsarecvfrom", err)
}
func (fd *netFD) Write(buf []byte) (int, error) {
n, err := fd.pfd.Write(buf)
runtime.KeepAlive(fd)
return n, wrapSyscallError("wsasend", err)
}
func (c *conn) writeBuffers(v *Buffers) (int64, error) {
if !c.ok() {
return 0, syscall.EINVAL
}
n, err := c.fd.writeBuffers(v)
if err != nil {
return n, &OpError{Op: "wsasend", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err}
}
return n, nil
}
func (fd *netFD) writeBuffers(buf *Buffers) (int64, error) {
n, err := fd.pfd.Writev((*[][]byte)(buf))
runtime.KeepAlive(fd)
return n, wrapSyscallError("wsasend", err)
}
func (fd *netFD) writeTo(buf []byte, sa syscall.Sockaddr) (int, error) {
n, err := fd.pfd.WriteTo(buf, sa)
runtime.KeepAlive(fd)
return n, wrapSyscallError("wsasendto", err)
}
func (fd *netFD) accept() (*netFD, error) {
s, rawsa, rsan, errcall, err := fd.pfd.Accept(func() (syscall.Handle, error) {
return sysSocket(fd.family, fd.sotype, 0)
})
if err != nil {
if errcall != "" {
err = wrapSyscallError(errcall, err)
}
return nil, err
}
// Associate our new socket with IOCP.
netfd, err := newFD(s, fd.family, fd.sotype, fd.net)
if err != nil {
poll.CloseFunc(s)
return nil, err
}
if err := netfd.init(); err != nil {
fd.Close()
return nil, err
}
// Get local and peer addr out of AcceptEx buffer.
var lrsa, rrsa *syscall.RawSockaddrAny
var llen, rlen int32
syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
0, rsan, rsan, &lrsa, &llen, &rrsa, &rlen)
lsa, _ := lrsa.Sockaddr()
rsa, _ := rrsa.Sockaddr()
netfd.setAddr(netfd.addrFunc()(lsa), netfd.addrFunc()(rsa))
return netfd, nil
}
// Unimplemented functions.
func (fd *netFD) dup() (*os.File, error) {
// TODO: Implement this
return nil, syscall.EWINDOWS
}
func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
return 0, 0, 0, nil, syscall.EWINDOWS
}
func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
return 0, 0, syscall.EWINDOWS
}