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 }