github.com/cbeuw/gotfo@v0.0.0-20180331191851-f2b091af84de/fd_go110_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.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  // internal/syscall/windows/syscall_windows.go
    61  type WSAMsg struct {
    62  	Name        *syscall.RawSockaddrAny
    63  	Namelen     int32
    64  	Buffers     *syscall.WSABuf
    65  	BufferCount uint32
    66  	Control     syscall.WSABuf
    67  	Flags       uint32
    68  }
    69  
    70  // operation contains superset of data necessary to perform all async IO.
    71  type operation struct {
    72  	// Used by IOCP interface, it must be first field
    73  	// of the struct, as our code rely on it.
    74  	o syscall.Overlapped
    75  
    76  	// fields used by runtime.netpoll
    77  	runtimeCtx uintptr
    78  	mode       int32
    79  	errno      int32
    80  	qty        uint32
    81  
    82  	// fields used only by net package
    83  	fd   *pollFD
    84  	errc chan error
    85  	buf  syscall.WSABuf
    86  	// new in go1.10 https://github.com/golang/go/commit/e49bc465a3acb2dd72e9afa5d40e541205c7d460
    87  	msg    WSAMsg
    88  	sa     syscall.Sockaddr
    89  	rsa    *syscall.RawSockaddrAny
    90  	rsan   int32
    91  	handle syscall.Handle
    92  	flags  uint32
    93  	bufs   []syscall.WSABuf
    94  }
    95  
    96  type pollFD struct {
    97  	// Lock sysfd and serialize access to Read and Write methods.
    98  	fdmu fdMutex
    99  
   100  	// System file descriptor. Immutable until Close.
   101  	sysfd syscall.Handle
   102  
   103  	// Read operation.
   104  	rop operation
   105  	// Write operation.
   106  	wop operation
   107  
   108  	// I/O poller.
   109  	pd pollDesc
   110  
   111  	// Used to implement pread/pwrite.
   112  	l sync.Mutex
   113  
   114  	// For console I/O.
   115  	isConsole      bool
   116  	lastbits       []byte   // first few bytes of the last incomplete rune in last write
   117  	readuint16     []uint16 // buffer to hold uint16s obtained with ReadConsole
   118  	readbyte       []byte   // buffer to hold decoding of readuint16 from utf16 to utf8
   119  	readbyteOffset int      // readbyte[readOffset:] is yet to be consumed with file.Read
   120  
   121  	// new in go1.10 https://github.com/golang/go/commit/382d4928b8a758a91f06de9e6cb10b92bb882eff#diff-618b3f21201d29fa6d82d50df02dcdba
   122  	csema uint32
   123  
   124  	skipSyncNotif bool
   125  
   126  	// Whether this is a streaming descriptor, as opposed to a
   127  	// packet-based descriptor like a UDP socket.
   128  	IsStream bool
   129  
   130  	// Whether a zero byte read indicates EOF. This is false for a
   131  	// message based socket connection.
   132  	ZeroReadIsEOF bool
   133  
   134  	// Whether this is a normal file.
   135  	isFile bool
   136  
   137  	// Whether this is a directory.
   138  	isDir bool
   139  }
   140  
   141  // Network file descriptor.
   142  type netFD struct {
   143  	pollFD
   144  
   145  	// immutable until Close
   146  	family      int
   147  	sotype      int
   148  	isConnected bool
   149  	net         string
   150  	laddr       net.Addr
   151  	raddr       net.Addr
   152  }
   153  
   154  func newFD(sysfd syscall.Handle, family int) (*netFD, error) {
   155  	if initErr != nil {
   156  		return nil, initErr
   157  	}
   158  
   159  	ret := &netFD{
   160  		pollFD: pollFD{
   161  			sysfd:         sysfd,
   162  			IsStream:      true,
   163  			ZeroReadIsEOF: true,
   164  		},
   165  		family: family,
   166  		sotype: syscall.SOCK_STREAM,
   167  		net:    "tcp",
   168  	}
   169  	return ret, nil
   170  }
   171  
   172  func (fd *netFD) init() error {
   173  	if initErr != nil {
   174  		return initErr
   175  	}
   176  
   177  	if err := fd.pd.init(fd); err != nil {
   178  		return err
   179  	}
   180  
   181  	// We do not use events, so we can skip them always.
   182  	flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
   183  	// It's not safe to skip completion notifications for UDP:
   184  	// http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx
   185  	if skipSyncNotif {
   186  		flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
   187  	}
   188  	err := syscall.SetFileCompletionNotificationModes(fd.sysfd, flags)
   189  	if err == nil && flags&syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS != 0 {
   190  		fd.skipSyncNotif = true
   191  	}
   192  
   193  	fd.rop.mode = 'r'
   194  	fd.wop.mode = 'w'
   195  	fd.rop.fd = &fd.pollFD
   196  	fd.wop.fd = &fd.pollFD
   197  	fd.rop.runtimeCtx = fd.pd.runtimeCtx
   198  	fd.wop.runtimeCtx = fd.pd.runtimeCtx
   199  
   200  	return nil
   201  }
   202  
   203  func (fd *pollFD) incref() error {
   204  	if !fd.fdmu.incref() {
   205  		return errClosing
   206  	}
   207  	return nil
   208  }
   209  
   210  // decref removes a reference from fd.
   211  // It also closes fd when the state of fd is set to closed and there
   212  // is no remaining reference.
   213  func (fd *pollFD) decref() error {
   214  	if fd.fdmu.decref() {
   215  		return fd.destroy()
   216  	}
   217  	return nil
   218  }
   219  
   220  func (fd *pollFD) destroy() error {
   221  	if fd.sysfd == syscall.InvalidHandle {
   222  		return syscall.EINVAL
   223  	}
   224  	// Poller may want to unregister fd in readiness notification mechanism,
   225  	// so this must be executed before fd.CloseFunc.
   226  	fd.pd.close()
   227  	var err error
   228  	if fd.isFile || fd.isConsole {
   229  		err = syscall.CloseHandle(fd.sysfd)
   230  	} else if fd.isDir {
   231  		err = syscall.FindClose(fd.sysfd)
   232  	} else {
   233  		// The net package uses the CloseFunc variable for testing.
   234  		err = syscall.Closesocket(fd.sysfd)
   235  	}
   236  	fd.sysfd = syscall.InvalidHandle
   237  	runtime_Semrelease(&fd.csema)
   238  	return err
   239  }
   240  
   241  // Close closes the FD. The underlying file descriptor is closed by
   242  // the destroy method when there are no remaining references.
   243  func (fd *pollFD) Close() error {
   244  	if !fd.fdmu.increfAndClose() {
   245  		return errClosing
   246  	}
   247  	// unblock pending reader and writer
   248  	fd.pd.evict()
   249  	err := fd.decref()
   250  	// Wait until the descriptor is closed. If this was the only
   251  	// reference, it is already closed.
   252  	runtime_Semacquire(&fd.csema)
   253  	return err
   254  }
   255  
   256  // Shutdown wraps the shutdown network call.
   257  func (fd *pollFD) Shutdown(how int) error {
   258  	if err := fd.incref(); err != nil {
   259  		return err
   260  	}
   261  	defer fd.decref()
   262  	return syscall.Shutdown(fd.sysfd, how)
   263  }