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 }