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