github.com/x04/go/src@v0.0.0-20200202162449-3d481ceb3525/runtime/netpoll_windows.go (about)

     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  	"github.com/x04/go/src/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 netpollIsPollDescriptor(fd uintptr) bool {
    45  	return fd == 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  func 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
    76  func 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  
   170  func 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  }