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 net.operation. Keep these in sync.
     16 type net_op struct {
     17 	// used by windows
     18 	o overlapped
     19 	// used by netpoll
     20 	pd    *pollDesc
     21 	mode  int32
     22 	errno int32
     23 	qty   uint32
     24 }
     25 
     26 type overlappedEntry struct {
     27 	key      uintptr
     28 	op       *net_op // In reality it's *overlapped, but we cast it to *net_op anyway.
     29 	internal uintptr
     30 	qty      uint32
     31 }
     32 
     33 var iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle
     34 
     35 func netpollinit() {
     36 	iocphandle = stdcall4(_CreateIoCompletionPort, _INVALID_HANDLE_VALUE, 0, 0, _DWORD_MAX)
     37 	if iocphandle == 0 {
     38 		println("netpoll: failed to create iocp handle (errno=", getlasterror(), ")")
     39 		throw("netpoll: failed to create iocp handle")
     40 	}
     41 }
     42 
     43 func netpollopen(fd uintptr, pd *pollDesc) int32 {
     44 	if stdcall4(_CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0 {
     45 		return -int32(getlasterror())
     46 	}
     47 	return 0
     48 }
     49 
     50 func netpollclose(fd uintptr) int32 {
     51 	// nothing to do
     52 	return 0
     53 }
     54 
     55 func netpollarm(pd *pollDesc, mode int) {
     56 	throw("unused")
     57 }
     58 
     59 // Polls for completed network IO.
     60 // Returns list of goroutines that become runnable.
     61 func netpoll(block bool) *g {
     62 	var entries [64]overlappedEntry
     63 	var wait, qty, key, flags, n, i uint32
     64 	var errno int32
     65 	var op *net_op
     66 	var gp guintptr
     67 
     68 	mp := getg().m
     69 
     70 	if iocphandle == _INVALID_HANDLE_VALUE {
     71 		return nil
     72 	}
     73 	wait = 0
     74 	if block {
     75 		wait = _INFINITE
     76 	}
     77 retry:
     78 	if _GetQueuedCompletionStatusEx != nil {
     79 		n = uint32(len(entries) / int(gomaxprocs))
     80 		if n < 8 {
     81 			n = 8
     82 		}
     83 		if block {
     84 			mp.blocked = true
     85 		}
     86 		if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
     87 			mp.blocked = false
     88 			errno = int32(getlasterror())
     89 			if !block && errno == _WAIT_TIMEOUT {
     90 				return nil
     91 			}
     92 			println("netpoll: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
     93 			throw("netpoll: GetQueuedCompletionStatusEx failed")
     94 		}
     95 		mp.blocked = false
     96 		for i = 0; i < n; i++ {
     97 			op = entries[i].op
     98 			errno = 0
     99 			qty = 0
    100 			if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
    101 				errno = int32(getlasterror())
    102 			}
    103 			handlecompletion(&gp, op, errno, qty)
    104 		}
    105 	} else {
    106 		op = nil
    107 		errno = 0
    108 		qty = 0
    109 		if block {
    110 			mp.blocked = true
    111 		}
    112 		if stdcall5(_GetQueuedCompletionStatus, iocphandle, uintptr(unsafe.Pointer(&qty)), uintptr(unsafe.Pointer(&key)), uintptr(unsafe.Pointer(&op)), uintptr(wait)) == 0 {
    113 			mp.blocked = false
    114 			errno = int32(getlasterror())
    115 			if !block && errno == _WAIT_TIMEOUT {
    116 				return nil
    117 			}
    118 			if op == nil {
    119 				println("netpoll: GetQueuedCompletionStatus failed (errno=", errno, ")")
    120 				throw("netpoll: GetQueuedCompletionStatus failed")
    121 			}
    122 			// dequeued failed IO packet, so report that
    123 		}
    124 		mp.blocked = false
    125 		handlecompletion(&gp, op, errno, qty)
    126 	}
    127 	if block && gp == 0 {
    128 		goto retry
    129 	}
    130 	return gp.ptr()
    131 }
    132 
    133 func handlecompletion(gpp *guintptr, op *net_op, errno int32, qty uint32) {
    134 	if op == nil {
    135 		throw("netpoll: GetQueuedCompletionStatus returned op == nil")
    136 	}
    137 	mode := op.mode
    138 	if mode != 'r' && mode != 'w' {
    139 		println("netpoll: GetQueuedCompletionStatus returned invalid mode=", mode)
    140 		throw("netpoll: GetQueuedCompletionStatus returned invalid mode")
    141 	}
    142 	op.errno = errno
    143 	op.qty = qty
    144 	netpollready(gpp, op.pd, mode)
    145 }
    146