URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [net/] [fd_windows.go] - Rev 747
Compare with Previous | Blame | View Log
// 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 netimport ("io""os""runtime""sync""syscall""time""unsafe")var initErr errorfunc init() {var d syscall.WSADatae := syscall.WSAStartup(uint32(0x202), &d)if e != nil {initErr = os.NewSyscallError("WSAStartup", e)}}func closesocket(s syscall.Handle) error {return syscall.Closesocket(s)}// Interface for all io operations.type anOpIface interface {Op() *anOpName() stringSubmit() error}// IO completion result parameters.type ioResult struct {qty uint32err error}// anOp implements functionality common to all io operations.type anOp struct {// Used by IOCP interface, it must be first field// of the struct, as our code rely on it.o syscall.Overlappedresultc chan ioResulterrnoc chan errorfd *netFD}func (o *anOp) Init(fd *netFD, mode int) {o.fd = fdvar i intif mode == 'r' {i = 0} else {i = 1}if fd.resultc[i] == nil {fd.resultc[i] = make(chan ioResult, 1)}o.resultc = fd.resultc[i]if fd.errnoc[i] == nil {fd.errnoc[i] = make(chan error)}o.errnoc = fd.errnoc[i]}func (o *anOp) Op() *anOp {return o}// bufOp is used by io operations that read / write// data from / to client buffer.type bufOp struct {anOpbuf syscall.WSABuf}func (o *bufOp) Init(fd *netFD, buf []byte, mode int) {o.anOp.Init(fd, mode)o.buf.Len = uint32(len(buf))if len(buf) == 0 {o.buf.Buf = nil} else {o.buf.Buf = (*byte)(unsafe.Pointer(&buf[0]))}}// resultSrv will retrieve all io completion results from// iocp and send them to the correspondent waiting client// goroutine via channel supplied in the request.type resultSrv struct {iocp syscall.Handle}func (s *resultSrv) Run() {var o *syscall.Overlappedvar key uint32var r ioResultfor {r.err = syscall.GetQueuedCompletionStatus(s.iocp, &(r.qty), &key, &o, syscall.INFINITE)switch {case r.err == nil:// Dequeued successfully completed io packet.case r.err == syscall.Errno(syscall.WAIT_TIMEOUT) && o == nil:// Wait has timed out (should not happen now, but might be used in the future).panic("GetQueuedCompletionStatus timed out")case o == nil:// Failed to dequeue anything -> report the error.panic("GetQueuedCompletionStatus failed " + r.err.Error())default:// Dequeued failed io packet.}(*anOp)(unsafe.Pointer(o)).resultc <- r}}// ioSrv executes net io requests.type ioSrv struct {submchan chan anOpIface // submit io requestscanchan chan anOpIface // cancel io requests}// ProcessRemoteIO will execute submit io requests on behalf// of other goroutines, all on a single os thread, so it can// cancel them later. Results of all operations will be sent// back to their requesters via channel supplied in request.func (s *ioSrv) ProcessRemoteIO() {runtime.LockOSThread()defer runtime.UnlockOSThread()for {select {case o := <-s.submchan:o.Op().errnoc <- o.Submit()case o := <-s.canchan:o.Op().errnoc <- syscall.CancelIo(syscall.Handle(o.Op().fd.sysfd))}}}// ExecIO executes a single io operation. It either executes it// 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) (int, error) {var err erroro := oi.Op()if deadline != 0 {// Send request to a special dedicated thread,// so it can stop the io with CancelIO later.s.submchan <- oierr = <-o.errnoc} else {err = oi.Submit()}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 = nildefault:return 0, &OpError{oi.Name(), o.fd.net, o.fd.laddr, err}}// Wait for our request to complete.var r ioResultif deadline != 0 {dt := deadline - time.Now().UnixNano()if dt < 1 {dt = 1}timer := time.NewTimer(time.Duration(dt) * time.Nanosecond)defer timer.Stop()select {case r = <-o.resultc:case <-timer.C:s.canchan <- oi<-o.errnocr = <-o.resultcif r.err == syscall.ERROR_OPERATION_ABORTED { // IO Canceledr.err = syscall.EWOULDBLOCK}}} else {r = <-o.resultc}if r.err != nil {err = &OpError{oi.Name(), o.fd.net, o.fd.laddr, r.err}}return int(r.qty), err}// Start helper goroutines.var resultsrv *resultSrvvar iosrv *ioSrvvar onceStartServer sync.Oncefunc startServer() {resultsrv = new(resultSrv)var err errorresultsrv.iocp, err = syscall.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 1)if err != nil {panic("CreateIoCompletionPort: " + err.Error())}go resultsrv.Run()iosrv = new(ioSrv)iosrv.submchan = make(chan anOpIface)iosrv.canchan = make(chan anOpIface)go iosrv.ProcessRemoteIO()}// Network file descriptor.type netFD struct {// locking/lifetime of sysfdsysmu sync.Mutexsysref intclosing bool// immutable until Closesysfd syscall.Handlefamily intsotype intisConnected boolnet stringladdr Addrraddr Addrresultc [2]chan ioResult // read/write completion resultserrnoc [2]chan error // read/write submit or cancel operation errors// owned by clientrdeadline int64rio sync.Mutexwdeadline int64wio sync.Mutex}func allocFD(fd syscall.Handle, family, sotype int, net string) *netFD {netfd := &netFD{sysfd: fd,family: family,sotype: sotype,net: net,}runtime.SetFinalizer(netfd, (*netFD).Close)return netfd}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 _, err := syscall.CreateIoCompletionPort(syscall.Handle(fd), resultsrv.iocp, 0, 0); err != nil {return nil, err}return allocFD(fd, family, proto, net), nil}func (fd *netFD) setAddr(laddr, raddr Addr) {fd.laddr = laddrfd.raddr = raddr}func (fd *netFD) connect(ra syscall.Sockaddr) error {return syscall.Connect(fd.sysfd, ra)}// Add a reference to this fd.func (fd *netFD) incref() {fd.sysmu.Lock()fd.sysref++fd.sysmu.Unlock()}// Remove a reference to this FD and close if we've been asked to do so (and// there are no references left.func (fd *netFD) decref() {fd.sysmu.Lock()fd.sysref--if fd.closing && fd.sysref == 0 && 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// use the resultsrv for Close too. Sigh.syscall.SetNonblock(fd.sysfd, false)closesocket(fd.sysfd)fd.sysfd = syscall.InvalidHandle// no need for a finalizer anymoreruntime.SetFinalizer(fd, nil)}fd.sysmu.Unlock()}func (fd *netFD) Close() error {if fd == nil || fd.sysfd == syscall.InvalidHandle {return os.EINVAL}fd.incref()syscall.Shutdown(fd.sysfd, syscall.SHUT_RDWR)fd.closing = truefd.decref()return nil}func (fd *netFD) shutdown(how int) error {if fd == nil || fd.sysfd == syscall.InvalidHandle {return os.EINVAL}err := syscall.Shutdown(fd.sysfd, how)if err != nil {return &OpError{"shutdown", fd.net, fd.laddr, err}}return nil}func (fd *netFD) CloseRead() error {return fd.shutdown(syscall.SHUT_RD)}func (fd *netFD) CloseWrite() error {return fd.shutdown(syscall.SHUT_WR)}// Read from network.type readOp struct {bufOp}func (o *readOp) Submit() error {var d, f uint32return syscall.WSARecv(syscall.Handle(o.fd.sysfd), &o.buf, 1, &d, &f, &o.o, nil)}func (o *readOp) Name() string {return "WSARecv"}func (fd *netFD) Read(buf []byte) (int, error) {if fd == nil {return 0, os.EINVAL}fd.rio.Lock()defer fd.rio.Unlock()fd.incref()defer fd.decref()if fd.sysfd == syscall.InvalidHandle {return 0, os.EINVAL}var o readOpo.Init(fd, buf, 'r')n, err := iosrv.ExecIO(&o, fd.rdeadline)if err == nil && n == 0 {err = io.EOF}return n, err}// ReadFrom from network.type readFromOp struct {bufOprsa syscall.RawSockaddrAnyrsan int32}func (o *readFromOp) Submit() error {var d, f uint32return syscall.WSARecvFrom(o.fd.sysfd, &o.buf, 1, &d, &f, &o.rsa, &o.rsan, &o.o, nil)}func (o *readFromOp) Name() string {return "WSARecvFrom"}func (fd *netFD) ReadFrom(buf []byte) (n int, sa syscall.Sockaddr, err error) {if fd == nil {return 0, nil, os.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}var o readFromOpo.Init(fd, buf, 'r')o.rsan = int32(unsafe.Sizeof(o.rsa))n, err = iosrv.ExecIO(&o, fd.rdeadline)if err != nil {return 0, nil, err}sa, _ = o.rsa.Sockaddr()return}// Write to network.type writeOp struct {bufOp}func (o *writeOp) Submit() error {var d uint32return syscall.WSASend(o.fd.sysfd, &o.buf, 1, &d, 0, &o.o, nil)}func (o *writeOp) Name() string {return "WSASend"}func (fd *netFD) Write(buf []byte) (int, error) {if fd == nil {return 0, os.EINVAL}fd.wio.Lock()defer fd.wio.Unlock()fd.incref()defer fd.decref()if fd.sysfd == syscall.InvalidHandle {return 0, os.EINVAL}var o writeOpo.Init(fd, buf, 'w')return iosrv.ExecIO(&o, fd.wdeadline)}// WriteTo to network.type writeToOp struct {bufOpsa syscall.Sockaddr}func (o *writeToOp) Submit() error {var d uint32return syscall.WSASendto(o.fd.sysfd, &o.buf, 1, &d, 0, o.sa, &o.o, nil)}func (o *writeToOp) Name() string {return "WSASendto"}func (fd *netFD) WriteTo(buf []byte, sa syscall.Sockaddr) (int, error) {if fd == nil {return 0, os.EINVAL}if len(buf) == 0 {return 0, nil}fd.wio.Lock()defer fd.wio.Unlock()fd.incref()defer fd.decref()if fd.sysfd == syscall.InvalidHandle {return 0, os.EINVAL}var o writeToOpo.Init(fd, buf, 'w')o.sa = sareturn iosrv.ExecIO(&o, fd.wdeadline)}// Accept new network connections.type acceptOp struct {anOpnewsock syscall.Handleattrs [2]syscall.RawSockaddrAny // space for local and remote address only}func (o *acceptOp) Submit() error {var d uint32l := uint32(unsafe.Sizeof(o.attrs[0]))return syscall.AcceptEx(o.fd.sysfd, o.newsock,(*byte)(unsafe.Pointer(&o.attrs[0])), 0, l, l, &d, &o.o)}func (o *acceptOp) Name() string {return "AcceptEx"}func (fd *netFD) accept(toAddr func(syscall.Sockaddr) Addr) (*netFD, error) {if fd == nil || fd.sysfd == syscall.InvalidHandle {return nil, os.EINVAL}fd.incref()defer fd.decref()// Get new socket.// See ../syscall/exec.go for description of ForkLock.syscall.ForkLock.RLock()s, err := syscall.Socket(fd.family, fd.sotype, 0)if err != nil {syscall.ForkLock.RUnlock()return nil, err}syscall.CloseOnExec(s)syscall.ForkLock.RUnlock()// Associate our new socket with IOCP.onceStartServer.Do(startServer)if _, err := syscall.CreateIoCompletionPort(s, resultsrv.iocp, 0, 0); err != nil {return nil, &OpError{"CreateIoCompletionPort", fd.net, fd.laddr, err}}// Submit accept request.var o acceptOpo.Init(fd, 'r')o.newsock = s_, err = iosrv.ExecIO(&o, 0)if err != nil {closesocket(s)return nil, err}// Inherit properties of the listening socket.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, err}// Get local and peer addr out of AcceptEx buffer.var lrsa, rrsa *syscall.RawSockaddrAnyvar llen, rlen int32l := uint32(unsafe.Sizeof(*lrsa))syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&o.attrs[0])),0, l, l, &lrsa, &llen, &rrsa, &rlen)lsa, _ := lrsa.Sockaddr()rsa, _ := rrsa.Sockaddr()netfd := allocFD(s, fd.family, fd.sotype, fd.net)netfd.setAddr(toAddr(lsa), toAddr(rsa))return netfd, nil}// Unimplemented functions.func (fd *netFD) dup() (*os.File, error) {// TODO: Implement thisreturn nil, os.NewSyscallError("dup", syscall.EWINDOWS)}func (fd *netFD) ReadMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) {return 0, 0, 0, nil, os.EAFNOSUPPORT}func (fd *netFD) WriteMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) {return 0, 0, os.EAFNOSUPPORT}
