github.com/cbeuw/gotfo@v0.0.0-20180331191851-f2b091af84de/fd_windows.go (about)

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package gotfo
     6  
     7  import (
     8  	"context"
     9  	"net"
    10  	"os"
    11  	"runtime"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  // ExecIO executes a single IO operation o. It submits and cancels
    17  // IO in the current thread for systems where Windows CancelIoEx API
    18  // is available. Alternatively, it passes the request onto
    19  // runtime netpoll and waits for completion or cancels request.
    20  func ExecIO(o *operation, name string, submit func(o *operation) error) (int, error) {
    21  	fd := o.fd
    22  	// Notify runtime netpoll about starting IO.
    23  	err := fd.pd.prepare(int(o.mode))
    24  	if err != nil {
    25  		return 0, err
    26  	}
    27  	// Start IO.
    28  	err = submit(o)
    29  
    30  	switch err {
    31  	case nil:
    32  		// IO completed immediately
    33  		if o.fd.skipSyncNotif {
    34  			// No completion message will follow, so return immediately.
    35  			return int(o.qty), nil
    36  		}
    37  		// Need to get our completion message anyway.
    38  	case syscall.ERROR_IO_PENDING:
    39  		// IO started, and we have to wait for its completion.
    40  		err = nil
    41  	default:
    42  		return 0, err
    43  	}
    44  	// Wait for our request to complete.
    45  	err = fd.pd.wait(int(o.mode))
    46  	if err == nil {
    47  		// All is good. Extract our IO results and return.
    48  		if o.errno != 0 {
    49  			err = syscall.Errno(o.errno)
    50  			return 0, err
    51  		}
    52  		return int(o.qty), nil
    53  	}
    54  	// IO is interrupted by "close" or "timeout"
    55  	netpollErr := err
    56  	switch netpollErr {
    57  	case errClosing, errTimeout:
    58  		// will deal with those.
    59  	default:
    60  		panic("net: unexpected runtime.netpoll error: " + netpollErr.Error())
    61  	}
    62  	// Cancel our request.
    63  	err = syscall.CancelIoEx(fd.sysfd, &o.o)
    64  
    65  	// Assuming ERROR_NOT_FOUND is returned, if IO is completed.
    66  	if err != nil && err != syscall.ERROR_NOT_FOUND {
    67  		// TODO(brainman): maybe do something else, but panic.
    68  		panic(err)
    69  	}
    70  
    71  	// Wait for cancelation to complete.
    72  	fd.pd.waitCanceled(int(o.mode))
    73  	if o.errno != 0 {
    74  		err = syscall.Errno(o.errno)
    75  		if err == syscall.ERROR_OPERATION_ABORTED { // IO Canceled
    76  			err = netpollErr
    77  		}
    78  		return 0, err
    79  	}
    80  	// We issued a cancelation request. But, it seems, IO operation succeeded
    81  	// before the cancelation request run. We need to treat the IO operation as
    82  	// succeeded (the bytes are actually sent/recv from network).
    83  	return int(o.qty), nil
    84  }
    85  
    86  func (fd *netFD) connect(ctx context.Context, ra syscall.Sockaddr, data []byte) error {
    87  	// Do not need to call fd.writeLock here,
    88  	// because fd is not yet accessible to user,
    89  	// so no concurrent operations are possible.
    90  	if err := fd.init(); err != nil {
    91  		return err
    92  	}
    93  	if deadline, ok := ctx.Deadline(); ok && !deadline.IsZero() {
    94  		fd.SetWriteDeadline(deadline)
    95  		defer fd.SetWriteDeadline(noDeadline)
    96  	}
    97  
    98  	// ConnectEx windows API requires an unconnected, previously bound socket.
    99  	var la syscall.Sockaddr
   100  
   101  	switch ra.(type) {
   102  	case *syscall.SockaddrInet4:
   103  		la = &syscall.SockaddrInet4{}
   104  	case *syscall.SockaddrInet6:
   105  		la = &syscall.SockaddrInet6{}
   106  	default:
   107  		panic("unexpected type in connect")
   108  	}
   109  	if err := syscall.Bind(fd.sysfd, la); err != nil {
   110  		return os.NewSyscallError("bind", err)
   111  	}
   112  
   113  	// Call ConnectEx API.
   114  	o := &fd.wop
   115  	o.sa = ra
   116  
   117  	// Wait for the goroutine converting context.Done into a write timeout
   118  	// to exist, otherwise our caller might cancel the context and
   119  	// cause fd.setWriteDeadline(aLongTimeAgo) to cancel a successful dial.
   120  	done := make(chan bool) // must be unbuffered
   121  	defer func() { done <- true }()
   122  	go func() {
   123  		select {
   124  		case <-ctx.Done():
   125  			// Force the runtime's poller to immediately give
   126  			// up waiting for writability.
   127  			fd.SetWriteDeadline(aLongTimeAgo)
   128  			<-done
   129  		case <-done:
   130  		}
   131  	}()
   132  
   133  	_, err := ExecIO(o, "ConnectEx", func(o *operation) error {
   134  		if data != nil {
   135  			var bytesSend uint32
   136  			return syscall.ConnectEx(o.fd.sysfd, o.sa, &data[0], uint32(len(data)), &bytesSend, &o.o)
   137  		} else {
   138  			return syscall.ConnectEx(o.fd.sysfd, o.sa, nil, 0, nil, &o.o)
   139  		}
   140  	})
   141  
   142  	if err != nil {
   143  		select {
   144  		case <-ctx.Done():
   145  			return mapErr(ctx.Err())
   146  		default:
   147  			if _, ok := err.(syscall.Errno); ok {
   148  				err = os.NewSyscallError("connectex", err)
   149  			}
   150  			return err
   151  		}
   152  	}
   153  	// Refresh socket properties.
   154  	return os.NewSyscallError("setsockopt", syscall.Setsockopt(fd.sysfd, syscall.SOL_SOCKET, syscall.SO_UPDATE_CONNECT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd))))
   155  }
   156  
   157  func (fd *netFD) acceptOne(rawsa []syscall.RawSockaddrAny, o *operation) (*netFD, error) {
   158  	// Get new socket.
   159  	s, err := syscall.Socket(fd.family, fd.sotype, 0)
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  
   164  	// Associate our new socket with IOCP.
   165  	netfd, err := newFD(s, fd.family)
   166  	if err != nil {
   167  		syscall.Close(s)
   168  		return nil, err
   169  	}
   170  	if err := netfd.init(); err != nil {
   171  		fd.Close()
   172  		return nil, err
   173  	}
   174  
   175  	// Submit accept request.
   176  	o.handle = s
   177  	o.rsan = int32(unsafe.Sizeof(rawsa[0]))
   178  	_, err = ExecIO(o, "AcceptEx", func(o *operation) error {
   179  		return syscall.AcceptEx(o.fd.sysfd, o.handle, (*byte)(unsafe.Pointer(&rawsa[0])), 0, uint32(o.rsan), uint32(o.rsan), &o.qty, &o.o)
   180  	})
   181  
   182  	if err != nil {
   183  		netfd.Close()
   184  		if _, ok := err.(syscall.Errno); ok {
   185  			err = os.NewSyscallError("acceptex", err)
   186  		}
   187  		return nil, err
   188  	}
   189  
   190  	// Inherit properties of the listening socket.
   191  	err = syscall.Setsockopt(s, syscall.SOL_SOCKET, syscall.SO_UPDATE_ACCEPT_CONTEXT, (*byte)(unsafe.Pointer(&fd.sysfd)), int32(unsafe.Sizeof(fd.sysfd)))
   192  	if err != nil {
   193  		netfd.Close()
   194  		return nil, os.NewSyscallError("setsockopt", err)
   195  	}
   196  	runtime.KeepAlive(fd)
   197  	return netfd, nil
   198  }
   199  
   200  func (fd *netFD) accept() (*netFD, error) {
   201  	if err := fd.readLock(); err != nil {
   202  		return nil, err
   203  	}
   204  	defer fd.readUnlock()
   205  
   206  	o := &fd.rop
   207  	var netfd *netFD
   208  	var err error
   209  	var rawsa [2]syscall.RawSockaddrAny
   210  	for {
   211  		netfd, err = fd.acceptOne(rawsa[:], o)
   212  		if err == nil {
   213  			break
   214  		}
   215  		// Sometimes we see WSAECONNRESET and ERROR_NETNAME_DELETED is
   216  		// returned here. These happen if connection reset is received
   217  		// before AcceptEx could complete. These errors relate to new
   218  		// connection, not to AcceptEx, so ignore broken connection and
   219  		// try AcceptEx again for more connections.
   220  		nerr, ok := err.(*os.SyscallError)
   221  		if !ok {
   222  			return nil, err
   223  		}
   224  		errno, ok := nerr.Err.(syscall.Errno)
   225  		if !ok {
   226  			return nil, err
   227  		}
   228  		switch errno {
   229  		case syscall.ERROR_NETNAME_DELETED, syscall.WSAECONNRESET:
   230  			// ignore these and try again
   231  		default:
   232  			return nil, err
   233  		}
   234  	}
   235  
   236  	// Get local and peer addr out of AcceptEx buffer.
   237  	var lrsa, rrsa *syscall.RawSockaddrAny
   238  	var llen, rlen int32
   239  	syscall.GetAcceptExSockaddrs((*byte)(unsafe.Pointer(&rawsa[0])),
   240  		0, uint32(o.rsan), uint32(o.rsan), &lrsa, &llen, &rrsa, &rlen)
   241  	lsa, _ := lrsa.Sockaddr()
   242  	rsa, _ := rrsa.Sockaddr()
   243  
   244  	netfd.setAddr(sockaddrToTCPAddr(lsa), sockaddrToTCPAddr(rsa))
   245  	return netfd, nil
   246  }
   247  
   248  func (fd *netFD) destroy() error {
   249  	if fd.sysfd == syscall.InvalidHandle {
   250  		return syscall.EINVAL
   251  	}
   252  	// Poller may want to unregister fd in readiness notification mechanism,
   253  	// so this must be executed before fd.syscall.Close.
   254  	fd.pd.close()
   255  
   256  	err := syscall.Close(fd.sysfd)
   257  	fd.sysfd = syscall.InvalidHandle
   258  	return err
   259  }
   260  
   261  func (fd *netFD) Close() error {
   262  	if !fd.fdmu.increfAndClose() {
   263  		return errClosing
   264  	}
   265  	// unblock pending reader and writer
   266  	fd.pd.evict()
   267  	fd.decref()
   268  	return nil
   269  }
   270  
   271  func (fd *netFD) shutdown(how int) error {
   272  	if err := fd.incref(); err != nil {
   273  		return err
   274  	}
   275  	defer fd.decref()
   276  	return syscall.Shutdown(fd.sysfd, how)
   277  }
   278  
   279  func (fd *netFD) setAddr(laddr, raddr net.Addr) {
   280  	fd.laddr = laddr
   281  	fd.raddr = raddr
   282  	runtime.SetFinalizer(fd, (*netFD).Close)
   283  }