net: implement non-blocking connect
Refactored bind/connect from sock.go into netFD.connect(), as
a consequence newFD() doesn't accept laddr/raddr anymore, and
expects an (optional) call to netFD.connect() followed by a
call to netFD.setAddr().
Windows code is updated, but still uses blocking connect,
since otherwise it needs support for ConnectEx syscall.
R=brainman, rsc
CC=golang-dev
https://golang.org/cl/4303060
diff --git a/src/pkg/net/fd.go b/src/pkg/net/fd.go
index df4dbce..3e87800 100644
--- a/src/pkg/net/fd.go
+++ b/src/pkg/net/fd.go
@@ -274,19 +274,25 @@
pollserver = p
}
-func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
+func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
onceStartServer.Do(startServer)
if e := syscall.SetNonblock(fd, true); e != 0 {
- return nil, &OpError{"setnonblock", net, laddr, os.Errno(e)}
+ return nil, os.Errno(e)
}
f = &netFD{
sysfd: fd,
family: family,
proto: proto,
net: net,
- laddr: laddr,
- raddr: raddr,
}
+ f.cr = make(chan bool, 1)
+ f.cw = make(chan bool, 1)
+ return f, nil
+}
+
+func (fd *netFD) setAddr(laddr, raddr Addr) {
+ fd.laddr = laddr
+ fd.raddr = raddr
var ls, rs string
if laddr != nil {
ls = laddr.String()
@@ -294,10 +300,31 @@
if raddr != nil {
rs = raddr.String()
}
- f.sysfile = os.NewFile(fd, net+":"+ls+"->"+rs)
- f.cr = make(chan bool, 1)
- f.cw = make(chan bool, 1)
- return f, nil
+ fd.sysfile = os.NewFile(fd.sysfd, fd.net+":"+ls+"->"+rs)
+}
+
+func (fd *netFD) connect(la, ra syscall.Sockaddr) (err os.Error) {
+ if la != nil {
+ e := syscall.Bind(fd.sysfd, la)
+ if e != 0 {
+ return os.Errno(e)
+ }
+ }
+ if ra != nil {
+ e := syscall.Connect(fd.sysfd, ra)
+ if e == syscall.EINPROGRESS {
+ var errno int
+ pollserver.WaitWrite(fd)
+ e, errno = syscall.GetsockoptInt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_ERROR)
+ if errno != 0 {
+ return os.NewSyscallError("getsockopt", errno)
+ }
+ }
+ if e != 0 {
+ return os.Errno(e)
+ }
+ }
+ return nil
}
// Add a reference to this fd.
@@ -593,10 +620,11 @@
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
- if nfd, err = newFD(s, fd.family, fd.proto, fd.net, fd.laddr, toAddr(sa)); err != nil {
+ if nfd, err = newFD(s, fd.family, fd.proto, fd.net); err != nil {
syscall.Close(s)
return nil, err
}
+ nfd.setAddr(fd.laddr, toAddr(sa))
return nfd, nil
}
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index 63a8fbc..0abf230 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -225,29 +225,48 @@
wio sync.Mutex
}
-func allocFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD) {
+func allocFD(fd, family, proto int, net string) (f *netFD) {
f = &netFD{
sysfd: fd,
family: family,
proto: proto,
net: net,
- laddr: laddr,
- raddr: raddr,
}
runtime.SetFinalizer(f, (*netFD).Close)
return f
}
-func newFD(fd, family, proto int, net string, laddr, raddr Addr) (f *netFD, err os.Error) {
+func newFD(fd, family, proto int, net string) (f *netFD, err os.Error) {
if initErr != nil {
return nil, initErr
}
onceStartServer.Do(startServer)
// Associate our socket with resultsrv.iocp.
if _, e := syscall.CreateIoCompletionPort(int32(fd), resultsrv.iocp, 0, 0); e != 0 {
- return nil, &OpError{"CreateIoCompletionPort", net, laddr, os.Errno(e)}
+ return nil, os.Errno(e)
}
- return allocFD(fd, family, proto, net, laddr, raddr), nil
+ return allocFD(fd, family, proto, net), nil
+}
+
+func (fd *netFD) setAddr(laddr, raddr Addr) {
+ fd.laddr = laddr
+ fd.raddr = raddr
+}
+
+func (fd *netFD) connect(la, ra syscall.Sockaddr) (err os.Error) {
+ if la != nil {
+ e := syscall.Bind(fd.sysfd, la)
+ if e != 0 {
+ return os.Errno(e)
+ }
+ }
+ if ra != nil {
+ e := syscall.Connect(fd.sysfd, ra)
+ if e != 0 {
+ return os.Errno(e)
+ }
+ }
+ return nil
}
// Add a reference to this fd.
@@ -497,7 +516,9 @@
lsa, _ := lrsa.Sockaddr()
rsa, _ := rrsa.Sockaddr()
- return allocFD(s, fd.family, fd.proto, fd.net, toAddr(lsa), toAddr(rsa)), nil
+ nfd = allocFD(s, fd.family, fd.proto, fd.net)
+ nfd.setAddr(toAddr(lsa), toAddr(rsa))
+ return nfd, nil
}
// Not implemeted functions.
diff --git a/src/pkg/net/file.go b/src/pkg/net/file.go
index 5439ed9..0e411a1 100644
--- a/src/pkg/net/file.go
+++ b/src/pkg/net/file.go
@@ -9,7 +9,7 @@
"syscall"
)
-func newFileFD(f *os.File) (*netFD, os.Error) {
+func newFileFD(f *os.File) (nfd *netFD, err os.Error) {
fd, errno := syscall.Dup(f.Fd())
if errno != 0 {
return nil, os.NewSyscallError("dup", errno)
@@ -50,7 +50,11 @@
sa, _ = syscall.Getpeername(fd)
raddr := toAddr(sa)
- return newFD(fd, 0, proto, laddr.Network(), laddr, raddr)
+ if nfd, err = newFD(fd, 0, proto, laddr.Network()); err != nil {
+ return nil, err
+ }
+ nfd.setAddr(laddr, raddr)
+ return nfd, nil
}
// FileConn returns a copy of the network connection corresponding to
diff --git a/src/pkg/net/sock.go b/src/pkg/net/sock.go
index 2681626..9b99ad5 100644
--- a/src/pkg/net/sock.go
+++ b/src/pkg/net/sock.go
@@ -44,33 +44,22 @@
syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_V6ONLY, 0)
}
- if la != nil {
- e = syscall.Bind(s, la)
- if e != 0 {
- closesocket(s)
- return nil, os.Errno(e)
- }
- }
-
- if ra != nil {
- e = syscall.Connect(s, ra)
- if e != 0 {
- closesocket(s)
- return nil, os.Errno(e)
- }
- }
-
- sa, _ := syscall.Getsockname(s)
- laddr := toAddr(sa)
- sa, _ = syscall.Getpeername(s)
- raddr := toAddr(sa)
-
- fd, err = newFD(s, f, p, net, laddr, raddr)
- if err != nil {
+ if fd, err = newFD(s, f, p, net); err != nil {
closesocket(s)
return nil, err
}
+ if err = fd.connect(la, ra); err != nil {
+ closesocket(s)
+ return nil, err
+ }
+
+ sa, _ := syscall.Getsockname(fd.sysfd)
+ laddr := toAddr(sa)
+ sa, _ = syscall.Getpeername(fd.sysfd)
+ raddr := toAddr(sa)
+
+ fd.setAddr(laddr, raddr)
return fd, nil
}