github.com/cbeuw/gotfo@v0.0.0-20180331191851-f2b091af84de/fd_go19_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.9,!go1.10
     6  
     7  package gotfo
     8  
     9  import (
    10  	"net"
    11  	"sync"
    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 = 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     *pollFD
    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  type pollFD struct {
    85  	// Lock sysfd and serialize access to Read and Write methods.
    86  	fdmu fdMutex
    87  
    88  	// System file descriptor. Immutable until Close.
    89  	sysfd syscall.Handle
    90  
    91  	// Read operation.
    92  	rop operation
    93  	// Write operation.
    94  	wop operation
    95  
    96  	// I/O poller.
    97  	pd pollDesc
    98  
    99  	// Used to implement pread/pwrite.
   100  	l sync.Mutex
   101  
   102  	// For console I/O.
   103  	isConsole      bool
   104  	lastbits       []byte   // first few bytes of the last incomplete rune in last write
   105  	readuint16     []uint16 // buffer to hold uint16s obtained with ReadConsole
   106  	readbyte       []byte   // buffer to hold decoding of readuint16 from utf16 to utf8
   107  	readbyteOffset int      // readbyte[readOffset:] is yet to be consumed with file.Read
   108  
   109  	skipSyncNotif bool
   110  
   111  	// Whether this is a streaming descriptor, as opposed to a
   112  	// packet-based descriptor like a UDP socket.
   113  	IsStream bool
   114  
   115  	// Whether a zero byte read indicates EOF. This is false for a
   116  	// message based socket connection.
   117  	ZeroReadIsEOF bool
   118  
   119  	// Whether this is a normal file.
   120  	isFile bool
   121  
   122  	// Whether this is a directory.
   123  	isDir bool
   124  }
   125  
   126  // Network file descriptor.
   127  type netFD struct {
   128  	pollFD
   129  
   130  	// immutable until Close
   131  	family      int
   132  	sotype      int
   133  	isConnected bool
   134  	net         string
   135  	laddr       net.Addr
   136  	raddr       net.Addr
   137  }
   138  
   139  func newFD(sysfd syscall.Handle, family int) (*netFD, error) {
   140  	if initErr != nil {
   141  		return nil, initErr
   142  	}
   143  
   144  	ret := &netFD{
   145  		pollFD: pollFD{
   146  			sysfd:         sysfd,
   147  			IsStream:      true,
   148  			ZeroReadIsEOF: true,
   149  		},
   150  		family: family,
   151  		sotype: syscall.SOCK_STREAM,
   152  		net:    "tcp",
   153  	}
   154  	return ret, nil
   155  }
   156  
   157  func (fd *netFD) init() error {
   158  	if initErr != nil {
   159  		return initErr
   160  	}
   161  
   162  	if err := fd.pd.init(fd); err != nil {
   163  		return err
   164  	}
   165  
   166  	// We do not use events, so we can skip them always.
   167  	flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
   168  	// It's not safe to skip completion notifications for UDP:
   169  	// http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx
   170  	if skipSyncNotif {
   171  		flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
   172  	}
   173  	err := syscall.SetFileCompletionNotificationModes(fd.sysfd, flags)
   174  	if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 {
   175  		fd.skipSyncNotif = true
   176  	}
   177  
   178  	fd.rop.mode = 'r'
   179  	fd.wop.mode = 'w'
   180  	fd.rop.fd = &fd.pollFD
   181  	fd.wop.fd = &fd.pollFD
   182  	fd.rop.runtimeCtx = fd.pd.runtimeCtx
   183  	fd.wop.runtimeCtx = fd.pd.runtimeCtx
   184  
   185  	return nil
   186  }