Source file
src/runtime/netpoll_windows.go
1
2
3
4
5 package runtime
6
7 import (
8 "internal/goarch"
9 "internal/runtime/atomic"
10 "internal/runtime/syscall/windows"
11 "unsafe"
12 )
13
14
15
16
17
18 const (
19 netpollSourceReady = iota + 1
20 netpollSourceBreak
21 netpollSourceTimer
22 )
23
24 const (
25
26
27
28
29 sourceBits = 4
30 sourceMasks = 1<<sourceBits - 1
31 )
32
33
34
35 func packNetpollKey(source uint8, pd *pollDesc) uintptr {
36
37 if source > (1<<sourceBits)-1 {
38
39 throw("runtime: source value is too large")
40 }
41 if goarch.PtrSize == 4 {
42 return uintptr(unsafe.Pointer(pd))<<sourceBits | uintptr(source)
43 }
44 return uintptr(taggedPointerPack(unsafe.Pointer(pd), uintptr(source)))
45 }
46
47
48 func unpackNetpollSource(key uintptr) uint8 {
49 if goarch.PtrSize == 4 {
50 return uint8(key & sourceMasks)
51 }
52 return uint8(taggedPointer(key).tag())
53 }
54
55
56
57 type pollOperation struct {
58
59 _ windows.Overlapped
60
61 pd *pollDesc
62 mode int32
63 }
64
65
66
67
68 func pollOperationFromOverlappedEntry(e *overlappedEntry) *pollOperation {
69 if e.ov == nil {
70 return nil
71 }
72 op := (*pollOperation)(unsafe.Pointer(e.ov))
73
74 var keyMatch bool
75 if goarch.PtrSize == 4 {
76 keyMatch = e.key&^sourceMasks == uintptr(unsafe.Pointer(op.pd))<<sourceBits
77 } else {
78 keyMatch = (*pollDesc)(taggedPointer(e.key).pointer()) == op.pd
79 }
80 if !keyMatch {
81 return nil
82 }
83 return op
84 }
85
86
87
88 type overlappedEntry struct {
89 key uintptr
90 ov *windows.Overlapped
91 internal uintptr
92 qty uint32
93 }
94
95 var (
96 iocphandle uintptr = windows.INVALID_HANDLE_VALUE
97
98 netpollWakeSig atomic.Uint32
99 )
100
101 func netpollinit() {
102 iocphandle = stdcall(_CreateIoCompletionPort, windows.INVALID_HANDLE_VALUE, 0, 0, windows.DWORD_MAX)
103 if iocphandle == 0 {
104 println("runtime: CreateIoCompletionPort failed (errno=", getlasterror(), ")")
105 throw("runtime: netpollinit failed")
106 }
107 }
108
109 func netpollIsPollDescriptor(fd uintptr) bool {
110 return fd == iocphandle
111 }
112
113 func netpollopen(fd uintptr, pd *pollDesc) int32 {
114 key := packNetpollKey(netpollSourceReady, pd)
115 if stdcall(_CreateIoCompletionPort, fd, iocphandle, key, 0) == 0 {
116 return int32(getlasterror())
117 }
118 return 0
119 }
120
121 func netpollclose(fd uintptr) int32 {
122
123 return 0
124 }
125
126 func netpollarm(pd *pollDesc, mode int) {
127 throw("runtime: unused")
128 }
129
130 func netpollBreak() {
131
132 if !netpollWakeSig.CompareAndSwap(0, 1) {
133 return
134 }
135
136 key := packNetpollKey(netpollSourceBreak, nil)
137 if stdcall(_PostQueuedCompletionStatus, iocphandle, 0, key, 0) == 0 {
138 println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")")
139 throw("runtime: netpoll: PostQueuedCompletionStatus failed")
140 }
141 }
142
143
144
145
146
147
148
149
150
151 func netpoll(delay int64) (gList, int32) {
152 if iocphandle == windows.INVALID_HANDLE_VALUE {
153 return gList{}, 0
154 }
155
156 var entries [64]overlappedEntry
157 var wait uint32
158 var toRun gList
159 mp := getg().m
160
161 if delay >= 1e15 {
162
163
164 delay = 1e15
165 }
166
167 if delay > 0 && mp.waitIocpHandle != 0 {
168
169
170
171
172
173
174 signaled := netpollQueueTimer(delay)
175 if signaled {
176
177
178 return gList{}, 0
179 }
180 }
181 if delay < 0 {
182 wait = windows.INFINITE
183 } else if delay == 0 {
184 wait = 0
185 } else if delay < 1e6 {
186 wait = 1
187 } else {
188 wait = uint32(delay / 1e6)
189 }
190 n := len(entries) / int(gomaxprocs)
191 if n < 8 {
192 n = 8
193 }
194 if delay != 0 {
195 mp.blocked = true
196 }
197 if stdcall(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
198 mp.blocked = false
199 errno := getlasterror()
200 if errno == windows.WAIT_TIMEOUT {
201 return gList{}, 0
202 }
203 println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
204 throw("runtime: netpoll failed")
205 }
206 mp.blocked = false
207 delta := int32(0)
208 for i := 0; i < n; i++ {
209 e := &entries[i]
210 switch unpackNetpollSource(e.key) {
211 case netpollSourceReady:
212 op := pollOperationFromOverlappedEntry(e)
213 if op == nil {
214
215 continue
216 }
217
218 mode := op.mode
219 if mode != 'r' && mode != 'w' {
220 println("runtime: GetQueuedCompletionStatusEx returned net_op with invalid mode=", mode)
221 throw("runtime: netpoll failed")
222 }
223 delta += netpollready(&toRun, op.pd, mode)
224 case netpollSourceBreak:
225 netpollWakeSig.Store(0)
226 if delay == 0 {
227
228 netpollBreak()
229 }
230 case netpollSourceTimer:
231
232 default:
233 println("runtime: GetQueuedCompletionStatusEx returned net_op with invalid key=", e.key)
234 throw("runtime: netpoll failed")
235 }
236 }
237 return toRun, delta
238 }
239
240
241
242 func netpollQueueTimer(delay int64) (signaled bool) {
243 mp := getg().m
244
245
246
247
248
249
250
251 errno := stdcall(_NtCancelWaitCompletionPacket, mp.waitIocpHandle, 1)
252 switch errno {
253 case windows.STATUS_CANCELLED:
254
255
256 fallthrough
257 case windows.STATUS_SUCCESS:
258 dt := -delay / 100
259 if stdcall(_SetWaitableTimer, mp.waitIocpTimer, uintptr(unsafe.Pointer(&dt)), 0, 0, 0, 0) == 0 {
260 println("runtime: SetWaitableTimer failed; errno=", getlasterror())
261 throw("runtime: netpoll failed")
262 }
263 key := packNetpollKey(netpollSourceTimer, nil)
264 if errno := stdcall(_NtAssociateWaitCompletionPacket, mp.waitIocpHandle, iocphandle, mp.waitIocpTimer, key, 0, 0, 0, uintptr(unsafe.Pointer(&signaled))); errno != 0 {
265 println("runtime: NtAssociateWaitCompletionPacket failed; errno=", errno)
266 throw("runtime: netpoll failed")
267 }
268 case windows.STATUS_PENDING:
269
270
271
272
273
274 default:
275 println("runtime: NtCancelWaitCompletionPacket failed; errno=", errno)
276 throw("runtime: netpoll failed")
277 }
278 return signaled
279 }
280
View as plain text