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