summaryrefslogtreecommitdiff
path: root/src/pkg/net/fd_windows.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/net/fd_windows.go')
-rw-r--r--src/pkg/net/fd_windows.go171
1 files changed, 96 insertions, 75 deletions
diff --git a/src/pkg/net/fd_windows.go b/src/pkg/net/fd_windows.go
index f00459f0b..45f5c2d88 100644
--- a/src/pkg/net/fd_windows.go
+++ b/src/pkg/net/fd_windows.go
@@ -5,6 +5,7 @@
package net
import (
+ "errors"
"io"
"os"
"runtime"
@@ -14,12 +15,6 @@ import (
"unsafe"
)
-type InvalidConnError struct{}
-
-func (e *InvalidConnError) Error() string { return "invalid net.Conn" }
-func (e *InvalidConnError) Temporary() bool { return false }
-func (e *InvalidConnError) Timeout() bool { return false }
-
var initErr error
func init() {
@@ -30,7 +25,7 @@ func init() {
}
}
-func closesocket(s syscall.Handle) (err error) {
+func closesocket(s syscall.Handle) error {
return syscall.Closesocket(s)
}
@@ -38,7 +33,7 @@ func closesocket(s syscall.Handle) (err error) {
type anOpIface interface {
Op() *anOp
Name() string
- Submit() (err error)
+ Submit() error
}
// IO completion result parameters.
@@ -153,24 +148,25 @@ func (s *ioSrv) ProcessRemoteIO() {
// inline, or, if a deadline is employed, passes the request onto
// a special goroutine and waits for completion or cancels request.
// deadline is unix nanos.
-func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (n int, err error) {
- var e error
+func (s *ioSrv) ExecIO(oi anOpIface, deadline int64) (int, error) {
+ var err error
o := oi.Op()
if deadline != 0 {
// Send request to a special dedicated thread,
// so it can stop the io with CancelIO later.
s.submchan <- oi
- e = <-o.errnoc
+ err = <-o.errnoc
} else {
- e = oi.Submit()
+ err = oi.Submit()
}
- switch e {
+ switch err {
case nil:
// IO completed immediately, but we need to get our completion message anyway.
case syscall.ERROR_IO_PENDING:
// IO started, and we have to wait for its completion.
+ err = nil
default:
- return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, e}
+ return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}
}
// Wait for our request to complete.
var r ioResult
@@ -245,25 +241,25 @@ type netFD struct {
wio sync.Mutex
}
-func allocFD(fd syscall.Handle, family, sotype int, net string) (f *netFD) {
- f = &netFD{
+func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {
+ netfd := &netFD{
sysfd: fd,
family: family,
sotype: sotype,
net: net,
}
- runtime.SetFinalizer(f, (*netFD).Close)
- return f
+ runtime.SetFinalizer(netfd, (*netFD).Close)
+ return netfd
}
-func newFD(fd syscall.Handle, family, proto int, net string) (f *netFD, err error) {
+func newFD(fd syscall.Handle, family, proto int, net string) (*netFD, error) {
if initErr != nil {
return nil, initErr
}
onceStartServer.Do(startServer)
// Associate our socket with resultsrv.iocp.
- if _, e := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); e != nil {
- return nil, e
+ if _, err := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); err != nil {
+ return nil, err
}
return allocFD(fd, family, proto, net), nil
}
@@ -273,15 +269,31 @@ func (fd *netFD) setAddr(laddr, raddr Addr) {
fd.raddr = raddr
}
-func (fd *netFD) connect(ra syscall.Sockaddr) (err error) {
+func (fd *netFD) connect(ra syscall.Sockaddr) error {
return syscall.Connect(fd.sysfd, ra)
}
+var errClosing = errors.New("use of closed network connection")
+
// Add a reference to this fd.
-func (fd *netFD) incref() {
+// If closing==true, mark the fd as closing.
+// Returns an error if the fd cannot be used.
+func (fd *netFD) incref(closing bool) error {
+ if fd == nil {
+ return errClosing
+ }
fd.sysmu.Lock()
+ if fd.closing {
+ fd.sysmu.Unlock()
+ return errClosing
+ }
fd.sysref++
+ if closing {
+ fd.closing = true
+ }
+ closing = fd.closing
fd.sysmu.Unlock()
+ return nil
}
// Remove a reference to this FD and close if we've been asked to do so (and
@@ -289,7 +301,17 @@ func (fd *netFD) incref() {
func (fd *netFD) decref() {
fd.sysmu.Lock()
fd.sysref--
- if fd.closing && fd.sysref == 0 && fd.sysfd != syscall.InvalidHandle {
+ // NOTE(rsc): On Unix we check fd.sysref == 0 here before closing,
+ // but on Windows we have no way to wake up the blocked I/O other
+ // than closing the socket (or calling Shutdown, which breaks other
+ // programs that might have a reference to the socket). So there is
+ // a small race here that we might close fd.sysfd and then some other
+ // goroutine might start a read of fd.sysfd (having read it before we
+ // write InvalidHandle to it), which might refer to some other file
+ // if the specific handle value gets reused. I think handle values on
+ // Windows are not reused as aggressively as file descriptors on Unix,
+ // so this might be tolerable.
+ if fd.closing && fd.sysfd != syscall.InvalidHandle {
// In case the user has set linger, switch to blocking mode so
// the close blocks. As long as this doesn't happen often, we
// can handle the extra OS processes. Otherwise we'll need to
@@ -304,20 +326,16 @@ func (fd *netFD) decref() {
}
func (fd *netFD) Close() error {
- if fd == nil || fd.sysfd == syscall.InvalidHandle {
- return os.EINVAL
+ if err := fd.incref(true); err != nil {
+ return err
}
-
- fd.incref()
- syscall.Shutdown(fd.sysfd, syscall.SHUT_RDWR)
- fd.closing = true
fd.decref()
return nil
}
func (fd *netFD) shutdown(how int) error {
if fd == nil || fd.sysfd == syscall.InvalidHandle {
- return os.EINVAL
+ return syscall.EINVAL
}
err := syscall.Shutdown(fd.sysfd, how)
if err != nil {
@@ -340,7 +358,7 @@ type readOp struct {
bufOp
}
-func (o *readOp) Submit() (err error) {
+func (o *readOp) Submit() error {
var d, f uint32
return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)
}
@@ -349,24 +367,26 @@ func (o *readOp) Name() string {
return "WSARecv"
}
-func (fd *netFD) Read(buf []byte) (n int, err error) {
+func (fd *netFD) Read(buf []byte) (int, error) {
if fd == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
fd.rio.Lock()
defer fd.rio.Unlock()
- fd.incref()
+ if err := fd.incref(false); err != nil {
+ return 0, err
+ }
defer fd.decref()
if fd.sysfd == syscall.InvalidHandle {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
var o readOp
o.Init(fd, buf, 'r')
- n, err = iosrv.ExecIO(&o, fd.rdeadline)
+ n, err := iosrv.ExecIO(&o, fd.rdeadline)
if err == nil && n == 0 {
err = io.EOF
}
- return
+ return n, err
}
// ReadFrom from network.
@@ -377,7 +397,7 @@ type readFromOp struct {
rsan int32
}
-func (o *readFromOp) Submit() (err error) {
+func (o *readFromOp) Submit() error {
var d, f uint32
return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil)
}
@@ -388,18 +408,17 @@ func (o *readFromOp) Name() string {
func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {
if fd == nil {
- return 0, nil, os.EINVAL
+ return 0, nil, syscall.EINVAL
}
if len(buf) == 0 {
return 0, nil, nil
}
fd.rio.Lock()
defer fd.rio.Unlock()
- fd.incref()
- defer fd.decref()
- if fd.sysfd == syscall.InvalidHandle {
- return 0, nil, os.EINVAL
+ if err := fd.incref(false); err != nil {
+ return 0, nil, err
}
+ defer fd.decref()
var o readFromOp
o.Init(fd, buf, 'r')
o.rsan = int32(unsafe.Sizeof(o.rsa))
@@ -417,7 +436,7 @@ type writeOp struct {
bufOp
}
-func (o *writeOp) Submit() (err error) {
+func (o *writeOp) Submit() error {
var d uint32
return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil)
}
@@ -426,17 +445,16 @@ func (o *writeOp) Name() string {
return "WSASend"
}
-func (fd *netFD) Write(buf []byte) (n int, err error) {
+func (fd *netFD) Write(buf []byte) (int, error) {
if fd == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
fd.wio.Lock()
defer fd.wio.Unlock()
- fd.incref()
- defer fd.decref()
- if fd.sysfd == syscall.InvalidHandle {
- return 0, os.EINVAL
+ if err := fd.incref(false); err != nil {
+ return 0, err
}
+ defer fd.decref()
var o writeOp
o.Init(fd, buf, 'w')
return iosrv.ExecIO(&o, fd.wdeadline)
@@ -449,7 +467,7 @@ type writeToOp struct {
sa syscall.Sockaddr
}
-func (o *writeToOp) Submit() (err error) {
+func (o *writeToOp) Submit() error {
var d uint32
return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil)
}
@@ -458,19 +476,21 @@ func (o *writeToOp) Name() string {
return "WSASendto"
}
-func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (n int, err error) {
+func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {
if fd == nil {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
if len(buf) == 0 {
return 0, nil
}
fd.wio.Lock()
defer fd.wio.Unlock()
- fd.incref()
+ if err := fd.incref(false); err != nil {
+ return 0, err
+ }
defer fd.decref()
if fd.sysfd == syscall.InvalidHandle {
- return 0, os.EINVAL
+ return 0, syscall.EINVAL
}
var o writeToOp
o.Init(fd, buf, 'w')
@@ -486,7 +506,7 @@ type acceptOp struct {
attrs [2]syscall.RawSockaddrAny // space for local and remote address only
}
-func (o *acceptOp) Submit() (err error) {
+func (o *acceptOp) Submit() error {
var d uint32
l := uint32(unsafe.Sizeof(o.attrs[0]))
return syscall.AcceptEx(o.fd.sysfd, o.newsock,
@@ -497,28 +517,27 @@ func (o *acceptOp) Name() string {
return "AcceptEx"
}
-func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err error) {
- if fd == nil || fd.sysfd == syscall.InvalidHandle {
- return nil, os.EINVAL
+func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {
+ if err := fd.incref(false); err != nil {
+ return nil, err
}
- fd.incref()
defer fd.decref()
// Get new socket.
// See ../syscall/exec.go for description of ForkLock.
syscall.ForkLock.RLock()
- s, e := syscall.Socket(fd.family, fd.sotype, 0)
- if e != nil {
+ s, err := syscall.Socket(fd.family, fd.sotype, 0)
+ if err != nil {
syscall.ForkLock.RUnlock()
- return nil, e
+ return nil, err
}
syscall.CloseOnExec(s)
syscall.ForkLock.RUnlock()
// Associate our new socket with IOCP.
onceStartServer.Do(startServer)
- if _, e = syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); e != nil {
- return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, e}
+ if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil {
+ return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err}
}
// Submit accept request.
@@ -532,10 +551,10 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
}
// Inherit properties of the listening socket.
- e = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
- if e != nil {
+ err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
+ if err != nil {
closesocket(s)
- return nil, e
+ return nil, err
}
// Get local and peer addr out of AcceptEx buffer.
@@ -547,22 +566,24 @@ func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (nfd *netFD, err err
lsa, _ := lrsa.Sockaddr()
rsa, _ := rrsa.Sockaddr()
- nfd = allocFD(s, fd.family, fd.sotype, fd.net)
- nfd.setAddr(toAddr(lsa), toAddr(rsa))
- return nfd, nil
+ netfd := allocFD(s, fd.family, fd.sotype, fd.net)
+ netfd.setAddr(toAddr(lsa), toAddr(rsa))
+ return netfd, nil
}
// Unimplemented functions.
-func (fd *netFD) dup() (f *os.File, err error) {
+func (fd *netFD) dup() (*os.File, error) {
// TODO: Implement this
return nil, os.NewSyscallError("dup", syscall.EWINDOWS)
}
+var errNoSupport = errors.New("address family not supported")
+
func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {
- return 0, 0, 0, nil, os.EAFNOSUPPORT
+ return 0, 0, 0, nil, errNoSupport
}
func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {
- return 0, 0, os.EAFNOSUPPORT
+ return 0, 0, errNoSupport
}