diff options
Diffstat (limited to 'src/pkg/net/fd_windows.go')
-rw-r--r-- | src/pkg/net/fd_windows.go | 171 |
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 } |