github.com/cbeuw/gotfo@v0.0.0-20180331191851-f2b091af84de/fd_go18_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  // +build go1.8,!go1.9
     6  
     7  package gotfo
     8  
     9  import (
    10  	"net"
    11  	"os"
    12  	"syscall"
    13  	"unsafe"
    14  )
    15  
    16  var (
    17  	initErr error
    18  )
    19  
    20  // CancelIo Windows API cancels all outstanding IO for a particular
    21  // socket on current thread. To overcome that limitation, we run
    22  // special goroutine, locked to OS single thread, that both starts
    23  // and cancels IO. It means, there are 2 unavoidable thread switches
    24  // for every IO.
    25  // Some newer versions of Windows has new CancelIoEx API, that does
    26  // not have that limitation and can be used from any thread. This
    27  // package uses CancelIoEx API, if present, otherwise it fallback
    28  // to CancelIo.
    29  
    30  var (
    31  	skipSyncNotif bool
    32  )
    33  
    34  func sysInit() {
    35  	var d syscall.WSAData
    36  	e := syscall.WSAStartup(uint32(0x202), &d)
    37  	if e != nil {
    38  		initErr = os.NewSyscallError("wsastartup", e)
    39  	}
    40  
    41  	// It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed:
    42  	// http://support.microsoft.com/kb/2568167
    43  	skipSyncNotif = true
    44  	protos := [2]int32{syscall.IPPROTO_TCP, 0}
    45  	var buf [32]syscall.WSAProtocolInfo
    46  	len := uint32(unsafe.Sizeof(buf))
    47  	n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len)
    48  	if err != nil {
    49  		skipSyncNotif = false
    50  	} else {
    51  		for i := int32(0); i < n; i++ {
    52  			if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 {
    53  				skipSyncNotif = false
    54  				break
    55  			}
    56  		}
    57  	}
    58  }
    59  
    60  // operation contains superset of data necessary to perform all async IO.
    61  type operation struct {
    62  	// Used by IOCP interface, it must be first field
    63  	// of the struct, as our code rely on it.
    64  	o syscall.Overlapped
    65  
    66  	// fields used by runtime.netpoll
    67  	runtimeCtx uintptr
    68  	mode       int32
    69  	errno      int32
    70  	qty        uint32
    71  
    72  	// fields used only by net package
    73  	fd     *netFD
    74  	errc   chan error
    75  	buf    syscall.WSABuf
    76  	sa     syscall.Sockaddr
    77  	rsa    *syscall.RawSockaddrAny
    78  	rsan   int32
    79  	handle syscall.Handle
    80  	flags  uint32
    81  	bufs   []syscall.WSABuf
    82  }
    83  
    84  // Network file descriptor.
    85  type netFD struct {
    86  	// locking/lifetime of sysfd + serialize access to Read and Write methods
    87  	fdmu fdMutex
    88  
    89  	// immutable until Close
    90  	sysfd         syscall.Handle
    91  	family        int
    92  	sotype        int
    93  	isStream      bool
    94  	isConnected   bool
    95  	skipSyncNotif bool
    96  	net           string
    97  	laddr         net.Addr
    98  	raddr         net.Addr
    99  
   100  	rop operation // read operation
   101  	wop operation // write operation
   102  
   103  	// wait server
   104  	pd pollDesc
   105  }
   106  
   107  func newFD(sysfd syscall.Handle, family int) (*netFD, error) {
   108  	if initErr != nil {
   109  		return nil, initErr
   110  	}
   111  
   112  	return &netFD{
   113  		sysfd:    sysfd,
   114  		family:   family,
   115  		sotype:   syscall.SOCK_STREAM,
   116  		net:      "tcp",
   117  		isStream: true,
   118  	}, nil
   119  }
   120  
   121  func (fd *netFD) init() error {
   122  	if err := fd.pd.init(fd); err != nil {
   123  		return err
   124  	}
   125  
   126  	// We do not use events, so we can skip them always.
   127  	flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
   128  	// It's not safe to skip completion notifications for UDP:
   129  	// http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx
   130  	if skipSyncNotif {
   131  		flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
   132  	}
   133  	err := syscall.SetFileCompletionNotificationModes(fd.sysfd, flags)
   134  	if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 {
   135  		fd.skipSyncNotif = true
   136  	}
   137  
   138  	fd.rop.mode = 'r'
   139  	fd.wop.mode = 'w'
   140  	fd.rop.fd = fd
   141  	fd.wop.fd = fd
   142  	fd.rop.runtimeCtx = fd.pd.runtimeCtx
   143  	fd.wop.runtimeCtx = fd.pd.runtimeCtx
   144  
   145  	return nil
   146  }