github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/netpoll_windows.go (about) 1 // Copyright 2013 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 runtime 6 7 import ( 8 "runtime/internal/atomic" 9 "unsafe" 10 ) 11 12 const _DWORD_MAX = 0xffffffff 13 14 const _INVALID_HANDLE_VALUE = ^uintptr(0) 15 16 // net_op must be the same as beginning of internal/poll.operation. 17 // Keep these in sync. 18 type net_op struct { 19 // used by windows 20 o overlapped 21 // used by netpoll 22 pd *pollDesc 23 mode int32 24 errno int32 25 qty uint32 26 } 27 28 type overlappedEntry struct { 29 key *pollDesc 30 op *net_op // In reality it's *overlapped, but we cast it to *net_op anyway. 31 internal uintptr 32 qty uint32 33 } 34 35 var ( 36 iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle 37 38 netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak 39 ) 40 41 func netpollinit() { 42 iocphandle = stdcall4(_CreateIoCompletionPort, _INVALID_HANDLE_VALUE, 0, 0, _DWORD_MAX) 43 if iocphandle == 0 { 44 println("runtime: CreateIoCompletionPort failed (errno=", getlasterror(), ")") 45 throw("runtime: netpollinit failed") 46 } 47 } 48 49 func netpollIsPollDescriptor(fd uintptr) bool { 50 return fd == iocphandle 51 } 52 53 func netpollopen(fd uintptr, pd *pollDesc) int32 { 54 // TODO(iant): Consider using taggedPointer on 64-bit systems. 55 if stdcall4(_CreateIoCompletionPort, fd, iocphandle, uintptr(unsafe.Pointer(pd)), 0) == 0 { 56 return int32(getlasterror()) 57 } 58 return 0 59 } 60 61 func netpollclose(fd uintptr) int32 { 62 // nothing to do 63 return 0 64 } 65 66 func netpollarm(pd *pollDesc, mode int) { 67 throw("runtime: unused") 68 } 69 70 func netpollBreak() { 71 // Failing to cas indicates there is an in-flight wakeup, so we're done here. 72 if !netpollWakeSig.CompareAndSwap(0, 1) { 73 return 74 } 75 76 if stdcall4(_PostQueuedCompletionStatus, iocphandle, 0, 0, 0) == 0 { 77 println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")") 78 throw("runtime: netpoll: PostQueuedCompletionStatus failed") 79 } 80 } 81 82 // netpoll checks for ready network connections. 83 // Returns list of goroutines that become runnable. 84 // delay < 0: blocks indefinitely 85 // delay == 0: does not block, just polls 86 // delay > 0: block for up to that many nanoseconds 87 func netpoll(delay int64) gList { 88 var entries [64]overlappedEntry 89 var wait, qty, flags, n, i uint32 90 var errno int32 91 var op *net_op 92 var toRun gList 93 94 mp := getg().m 95 96 if iocphandle == _INVALID_HANDLE_VALUE { 97 return gList{} 98 } 99 if delay < 0 { 100 wait = _INFINITE 101 } else if delay == 0 { 102 wait = 0 103 } else if delay < 1e6 { 104 wait = 1 105 } else if delay < 1e15 { 106 wait = uint32(delay / 1e6) 107 } else { 108 // An arbitrary cap on how long to wait for a timer. 109 // 1e9 ms == ~11.5 days. 110 wait = 1e9 111 } 112 113 n = uint32(len(entries) / int(gomaxprocs)) 114 if n < 8 { 115 n = 8 116 } 117 if delay != 0 { 118 mp.blocked = true 119 } 120 if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 { 121 mp.blocked = false 122 errno = int32(getlasterror()) 123 if errno == _WAIT_TIMEOUT { 124 return gList{} 125 } 126 println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")") 127 throw("runtime: netpoll failed") 128 } 129 mp.blocked = false 130 for i = 0; i < n; i++ { 131 op = entries[i].op 132 if op != nil && op.pd == entries[i].key { 133 errno = 0 134 qty = 0 135 if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 { 136 errno = int32(getlasterror()) 137 } 138 handlecompletion(&toRun, op, errno, qty) 139 } else { 140 netpollWakeSig.Store(0) 141 if delay == 0 { 142 // Forward the notification to the 143 // blocked poller. 144 netpollBreak() 145 } 146 } 147 } 148 return toRun 149 } 150 151 func handlecompletion(toRun *gList, op *net_op, errno int32, qty uint32) { 152 mode := op.mode 153 if mode != 'r' && mode != 'w' { 154 println("runtime: GetQueuedCompletionStatusEx returned invalid mode=", mode) 155 throw("runtime: netpoll failed") 156 } 157 op.errno = errno 158 op.qty = qty 159 netpollready(toRun, op.pd, mode) 160 }