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