Home | History | Annotate | Download | only in runtime
      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 
      5 package runtime
      6 
      7 import (
      8 	"unsafe"
      9 )
     10 
     11 const _DWORD_MAX = 0xffffffff
     12 
     13 const _INVALID_HANDLE_VALUE = ^uintptr(0)
     14 
     15 // net_op must be the same as beginning of internal/poll.operation.
     16 // Keep these in sync.
     17 type 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 
     27 type 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 
     34 var iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle
     35 
     36 func 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 
     44 func netpolldescriptor() uintptr {
     45 	return iocphandle
     46 }
     47 
     48 func 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 
     55 func netpollclose(fd uintptr) int32 {
     56 	// nothing to do
     57 	return 0
     58 }
     59 
     60 func 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.
     66 func 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 	}
     82 retry:
     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 
    138 func 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