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 netpolldescriptor() uintptr {
45	return 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
64// Polls for completed network IO.
65// Returns list of goroutines that become runnable.
66func netpoll(block bool) *g {
67	var entries [64]overlappedEntry
68	var wait, qty, key, flags, n, i uint32
69	var errno int32
70	var op *net_op
71	var gp guintptr
72
73	mp := getg().m
74
75	if iocphandle == _INVALID_HANDLE_VALUE {
76		return nil
77	}
78	wait = 0
79	if block {
80		wait = _INFINITE
81	}
82retry:
83	if _GetQueuedCompletionStatusEx != nil {
84		n = uint32(len(entries) / int(gomaxprocs))
85		if n < 8 {
86			n = 8
87		}
88		if block {
89			mp.blocked = true
90		}
91		if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
92			mp.blocked = false
93			errno = int32(getlasterror())
94			if !block && errno == _WAIT_TIMEOUT {
95				return nil
96			}
97			println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
98			throw("runtime: netpoll failed")
99		}
100		mp.blocked = false
101		for i = 0; i < n; i++ {
102			op = entries[i].op
103			errno = 0
104			qty = 0
105			if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
106				errno = int32(getlasterror())
107			}
108			handlecompletion(&gp, op, errno, qty)
109		}
110	} else {
111		op = nil
112		errno = 0
113		qty = 0
114		if block {
115			mp.blocked = true
116		}
117		if stdcall5(_GetQueuedCompletionStatus, iocphandle, uintptr(unsafe.Pointer(&qty)), uintptr(unsafe.Pointer(&key)), uintptr(unsafe.Pointer(&op)), uintptr(wait)) == 0 {
118			mp.blocked = false
119			errno = int32(getlasterror())
120			if !block && errno == _WAIT_TIMEOUT {
121				return nil
122			}
123			if op == nil {
124				println("runtime: GetQueuedCompletionStatus failed (errno=", errno, ")")
125				throw("runtime: netpoll failed")
126			}
127			// dequeued failed IO packet, so report that
128		}
129		mp.blocked = false
130		handlecompletion(&gp, op, errno, qty)
131	}
132	if block && gp == 0 {
133		goto retry
134	}
135	return gp.ptr()
136}
137
138func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) {
139	if op == nil {
140		println("runtime: GetQueuedCompletionStatus returned op == nil")
141		throw("runtime: netpoll failed")
142	}
143	mode := op.mode
144	if mode != 'r' && mode != 'w' {
145		println("runtime: GetQueuedCompletionStatus returned invalid mode=", mode)
146		throw("runtime: netpoll failed")
147	}
148	op.errno = errno
149	op.qty = qty
150	netpollready(gpp, op.pd, mode)
151}
152