1
0
mirror of https://github.com/golang/go synced 2024-11-19 11:04:47 -07:00
go/src/runtime/netpoll_windows.go

152 lines
3.6 KiB
Go
Raw Normal View History

// 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")
}
}
os: use poller for file I/O This changes the os package to use the runtime poller for file I/O where possible. When a system call blocks on a pollable descriptor, the goroutine will be blocked on the poller but the thread will be released to run other goroutines. When using a non-pollable descriptor, the os package will continue to use thread-blocking system calls as before. For example, on GNU/Linux, the runtime poller uses epoll. epoll does not support ordinary disk files, so they will continue to use blocking I/O as before. The poller will be used for pipes. Since this means that the poller is used for many more programs, this modifies the runtime to only block waiting for the poller if there is some goroutine that is waiting on the poller. Otherwise, there is no point, as the poller will never make any goroutine ready. This preserves the runtime's current simple deadlock detection. This seems to crash FreeBSD systems, so it is disabled on FreeBSD. This is issue 19093. Using the poller on Windows requires opening the file with FILE_FLAG_OVERLAPPED. We should only do that if we can remove that flag if the program calls the Fd method. This is issue 19098. Update #6817. Update #7903. Update #15021. Update #18507. Update #19093. Update #19098. Change-Id: Ia5197dcefa7c6fbcca97d19a6f8621b2abcbb1fe Reviewed-on: https://go-review.googlesource.com/36800 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
2017-02-10 16:17:38 -07:00
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)
}