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 func netpoll(delay int64) (gList, int32) {
152 if iocphandle == _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 = _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 stdcall6(_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 == _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 const (
244 STATUS_SUCCESS = 0x00000000
245 STATUS_PENDING = 0x00000103
246 STATUS_CANCELLED = 0xC0000120
247 )
248 mp := getg().m
249
250
251
252
253
254
255
256 errno := stdcall2(_NtCancelWaitCompletionPacket, mp.waitIocpHandle, 1)
257 switch errno {
258 case STATUS_CANCELLED:
259
260
261 fallthrough
262 case STATUS_SUCCESS:
263 dt := -delay / 100
264 if stdcall6(_SetWaitableTimer, mp.waitIocpTimer, uintptr(unsafe.Pointer(&dt)), 0, 0, 0, 0) == 0 {
265 println("runtime: SetWaitableTimer failed; errno=", getlasterror())
266 throw("runtime: netpoll failed")
267 }
268 key := packNetpollKey(netpollSourceTimer, nil)
269 if errno := stdcall8(_NtAssociateWaitCompletionPacket, mp.waitIocpHandle, iocphandle, mp.waitIocpTimer, key, 0, 0, 0, uintptr(unsafe.Pointer(&signaled))); errno != 0 {
270 println("runtime: NtAssociateWaitCompletionPacket failed; errno=", errno)
271 throw("runtime: netpoll failed")
272 }
273 case STATUS_PENDING:
274
275
276
277
278
279 default:
280 println("runtime: NtCancelWaitCompletionPacket failed; errno=", errno)
281 throw("runtime: netpoll failed")
282 }
283 return signaled
284 }
285
View as plain text