|  | // Copyright 2013 The Go Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style | 
|  | // license that can be found in the LICENSE file. | 
|  |  | 
|  | package runtime | 
|  |  | 
|  | import ( | 
|  | "unsafe" | 
|  | ) | 
|  |  | 
|  | const _DWORD_MAX = 0xffffffff | 
|  |  | 
|  | const _INVALID_HANDLE_VALUE = ^uintptr(0) | 
|  |  | 
|  | // net_op must be the same as beginning of internal/poll.operation. | 
|  | // Keep these in sync. | 
|  | type net_op struct { | 
|  | // used by windows | 
|  | o overlapped | 
|  | // used by netpoll | 
|  | pd    *pollDesc | 
|  | mode  int32 | 
|  | errno int32 | 
|  | qty   uint32 | 
|  | } | 
|  |  | 
|  | type overlappedEntry struct { | 
|  | key      uintptr | 
|  | op       *net_op // In reality it's *overlapped, but we cast it to *net_op anyway. | 
|  | internal uintptr | 
|  | qty      uint32 | 
|  | } | 
|  |  | 
|  | var iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle | 
|  |  | 
|  | func netpollinit() { | 
|  | iocphandle = stdcall4(_CreateIoCompletionPort, _INVALID_HANDLE_VALUE, 0, 0, _DWORD_MAX) | 
|  | if iocphandle == 0 { | 
|  | println("runtime: CreateIoCompletionPort failed (errno=", getlasterror(), ")") | 
|  | throw("runtime: netpollinit failed") | 
|  | } | 
|  | } | 
|  |  | 
|  | func netpolldescriptor() uintptr { | 
|  | return iocphandle | 
|  | } | 
|  |  | 
|  | func netpollopen(fd uintptr, pd *pollDesc) int32 { | 
|  | if stdcall4(_CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0 { | 
|  | return int32(getlasterror()) | 
|  | } | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | func netpollclose(fd uintptr) int32 { | 
|  | // nothing to do | 
|  | return 0 | 
|  | } | 
|  |  | 
|  | func netpollarm(pd *pollDesc, mode int) { | 
|  | throw("runtime: unused") | 
|  | } | 
|  |  | 
|  | // Polls for completed network IO. | 
|  | // Returns list of goroutines that become runnable. | 
|  | func netpoll(block bool) *g { | 
|  | var entries [64]overlappedEntry | 
|  | var wait, qty, key, flags, n, i uint32 | 
|  | var errno int32 | 
|  | var op *net_op | 
|  | var gp guintptr | 
|  |  | 
|  | mp := getg().m | 
|  |  | 
|  | if iocphandle == _INVALID_HANDLE_VALUE { | 
|  | return nil | 
|  | } | 
|  | wait = 0 | 
|  | if block { | 
|  | wait = _INFINITE | 
|  | } | 
|  | retry: | 
|  | if _GetQueuedCompletionStatusEx != nil { | 
|  | n = uint32(len(entries) / int(gomaxprocs)) | 
|  | if n < 8 { | 
|  | n = 8 | 
|  | } | 
|  | if block { | 
|  | mp.blocked = true | 
|  | } | 
|  | if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 { | 
|  | mp.blocked = false | 
|  | errno = int32(getlasterror()) | 
|  | if !block && errno == _WAIT_TIMEOUT { | 
|  | return nil | 
|  | } | 
|  | println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")") | 
|  | throw("runtime: netpoll failed") | 
|  | } | 
|  | mp.blocked = false | 
|  | for i = 0; i < n; i++ { | 
|  | op = entries[i].op | 
|  | errno = 0 | 
|  | qty = 0 | 
|  | if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 { | 
|  | errno = int32(getlasterror()) | 
|  | } | 
|  | handlecompletion(&gp, op, errno, qty) | 
|  | } | 
|  | } else { | 
|  | op = nil | 
|  | errno = 0 | 
|  | qty = 0 | 
|  | if block { | 
|  | mp.blocked = true | 
|  | } | 
|  | if stdcall5(_GetQueuedCompletionStatus, iocphandle, uintptr(unsafe.Pointer(&qty)), uintptr(unsafe.Pointer(&key)), uintptr(unsafe.Pointer(&op)), uintptr(wait)) == 0 { | 
|  | mp.blocked = false | 
|  | errno = int32(getlasterror()) | 
|  | if !block && errno == _WAIT_TIMEOUT { | 
|  | return nil | 
|  | } | 
|  | if op == nil { | 
|  | println("runtime: GetQueuedCompletionStatus failed (errno=", errno, ")") | 
|  | throw("runtime: netpoll failed") | 
|  | } | 
|  | // dequeued failed IO packet, so report that | 
|  | } | 
|  | mp.blocked = false | 
|  | handlecompletion(&gp, op, errno, qty) | 
|  | } | 
|  | if block && gp == 0 { | 
|  | goto retry | 
|  | } | 
|  | return gp.ptr() | 
|  | } | 
|  |  | 
|  | func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) { | 
|  | if op == nil { | 
|  | println("runtime: GetQueuedCompletionStatus returned op == nil") | 
|  | throw("runtime: netpoll failed") | 
|  | } | 
|  | mode := op.mode | 
|  | if mode != 'r' && mode != 'w' { | 
|  | println("runtime: GetQueuedCompletionStatus returned invalid mode=", mode) | 
|  | throw("runtime: netpoll failed") | 
|  | } | 
|  | op.errno = errno | 
|  | op.qty = qty | 
|  | netpollready(gpp, op.pd, mode) | 
|  | } |