Source file src/runtime/netpoll_windows.go

     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  	"runtime/internal/atomic"
     9  	"unsafe"
    10  )
    11  
    12  const _DWORD_MAX = 0xffffffff
    13  
    14  const _INVALID_HANDLE_VALUE = ^uintptr(0)
    15  
    16  // net_op must be the same as beginning of internal/poll.operation.
    17  // Keep these in sync.
    18  type net_op struct {
    19  	// used by windows
    20  	o overlapped
    21  	// used by netpoll
    22  	pd    *pollDesc
    23  	mode  int32
    24  	errno int32
    25  	qty   uint32
    26  }
    27  
    28  type overlappedEntry struct {
    29  	key      *pollDesc
    30  	op       *net_op // In reality it's *overlapped, but we cast it to *net_op anyway.
    31  	internal uintptr
    32  	qty      uint32
    33  }
    34  
    35  var (
    36  	iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle
    37  
    38  	netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak
    39  )
    40  
    41  func netpollinit() {
    42  	iocphandle = stdcall4(_CreateIoCompletionPort, _INVALID_HANDLE_VALUE, 0, 0, _DWORD_MAX)
    43  	if iocphandle == 0 {
    44  		println("runtime: CreateIoCompletionPort failed (errno=", getlasterror(), ")")
    45  		throw("runtime: netpollinit failed")
    46  	}
    47  }
    48  
    49  func netpollIsPollDescriptor(fd uintptr) bool {
    50  	return fd == iocphandle
    51  }
    52  
    53  func netpollopen(fd uintptr, pd *pollDesc) int32 {
    54  	// TODO(iant): Consider using taggedPointer on 64-bit systems.
    55  	if stdcall4(_CreateIoCompletionPort, fd, iocphandle, uintptr(unsafe.Pointer(pd)), 0) == 0 {
    56  		return int32(getlasterror())
    57  	}
    58  	return 0
    59  }
    60  
    61  func netpollclose(fd uintptr) int32 {
    62  	// nothing to do
    63  	return 0
    64  }
    65  
    66  func netpollarm(pd *pollDesc, mode int) {
    67  	throw("runtime: unused")
    68  }
    69  
    70  func netpollBreak() {
    71  	// Failing to cas indicates there is an in-flight wakeup, so we're done here.
    72  	if !netpollWakeSig.CompareAndSwap(0, 1) {
    73  		return
    74  	}
    75  
    76  	if stdcall4(_PostQueuedCompletionStatus, iocphandle, 0, 0, 0) == 0 {
    77  		println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")")
    78  		throw("runtime: netpoll: PostQueuedCompletionStatus failed")
    79  	}
    80  }
    81  
    82  // netpoll checks for ready network connections.
    83  // Returns list of goroutines that become runnable.
    84  // delay < 0: blocks indefinitely
    85  // delay == 0: does not block, just polls
    86  // delay > 0: block for up to that many nanoseconds
    87  func netpoll(delay int64) (gList, int32) {
    88  	var entries [64]overlappedEntry
    89  	var wait, qty, flags, n, i uint32
    90  	var errno int32
    91  	var op *net_op
    92  	var toRun gList
    93  
    94  	mp := getg().m
    95  
    96  	if iocphandle == _INVALID_HANDLE_VALUE {
    97  		return gList{}, 0
    98  	}
    99  	if delay < 0 {
   100  		wait = _INFINITE
   101  	} else if delay == 0 {
   102  		wait = 0
   103  	} else if delay < 1e6 {
   104  		wait = 1
   105  	} else if delay < 1e15 {
   106  		wait = uint32(delay / 1e6)
   107  	} else {
   108  		// An arbitrary cap on how long to wait for a timer.
   109  		// 1e9 ms == ~11.5 days.
   110  		wait = 1e9
   111  	}
   112  
   113  	n = uint32(len(entries) / int(gomaxprocs))
   114  	if n < 8 {
   115  		n = 8
   116  	}
   117  	if delay != 0 {
   118  		mp.blocked = true
   119  	}
   120  	if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
   121  		mp.blocked = false
   122  		errno = int32(getlasterror())
   123  		if errno == _WAIT_TIMEOUT {
   124  			return gList{}, 0
   125  		}
   126  		println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
   127  		throw("runtime: netpoll failed")
   128  	}
   129  	mp.blocked = false
   130  	delta := int32(0)
   131  	for i = 0; i < n; i++ {
   132  		op = entries[i].op
   133  		if op != nil && op.pd == entries[i].key {
   134  			errno = 0
   135  			qty = 0
   136  			if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
   137  				errno = int32(getlasterror())
   138  			}
   139  			delta += handlecompletion(&toRun, op, errno, qty)
   140  		} else {
   141  			netpollWakeSig.Store(0)
   142  			if delay == 0 {
   143  				// Forward the notification to the
   144  				// blocked poller.
   145  				netpollBreak()
   146  			}
   147  		}
   148  	}
   149  	return toRun, delta
   150  }
   151  
   152  func handlecompletion(toRun *gList, op *net_op, errno int32, qty uint32) int32 {
   153  	mode := op.mode
   154  	if mode != 'r' && mode != 'w' {
   155  		println("runtime: GetQueuedCompletionStatusEx returned invalid mode=", mode)
   156  		throw("runtime: netpoll failed")
   157  	}
   158  	op.errno = errno
   159  	op.qty = qty
   160  	return netpollready(toRun, op.pd, mode)
   161  }
   162  

View as plain text