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      uintptr
    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  	if stdcall4(_CreateIoCompletionPort, fd, iocphandle, 0, 0) == 0 {
    55  		return int32(getlasterror())
    56  	}
    57  	return 0
    58  }
    59  
    60  func netpollclose(fd uintptr) int32 {
    61  	// nothing to do
    62  	return 0
    63  }
    64  
    65  func netpollarm(pd *pollDesc, mode int) {
    66  	throw("runtime: unused")
    67  }
    68  
    69  func netpollBreak() {
    70  	// Failing to cas indicates there is an in-flight wakeup, so we're done here.
    71  	if !netpollWakeSig.CompareAndSwap(0, 1) {
    72  		return
    73  	}
    74  
    75  	if stdcall4(_PostQueuedCompletionStatus, iocphandle, 0, 0, 0) == 0 {
    76  		println("runtime: netpoll: PostQueuedCompletionStatus failed (errno=", getlasterror(), ")")
    77  		throw("runtime: netpoll: PostQueuedCompletionStatus failed")
    78  	}
    79  }
    80  
    81  // netpoll checks for ready network connections.
    82  // Returns list of goroutines that become runnable.
    83  // delay < 0: blocks indefinitely
    84  // delay == 0: does not block, just polls
    85  // delay > 0: block for up to that many nanoseconds
    86  func netpoll(delay int64) gList {
    87  	var entries [64]overlappedEntry
    88  	var wait, qty, flags, n, i uint32
    89  	var errno int32
    90  	var op *net_op
    91  	var toRun gList
    92  
    93  	mp := getg().m
    94  
    95  	if iocphandle == _INVALID_HANDLE_VALUE {
    96  		return gList{}
    97  	}
    98  	if delay < 0 {
    99  		wait = _INFINITE
   100  	} else if delay == 0 {
   101  		wait = 0
   102  	} else if delay < 1e6 {
   103  		wait = 1
   104  	} else if delay < 1e15 {
   105  		wait = uint32(delay / 1e6)
   106  	} else {
   107  		// An arbitrary cap on how long to wait for a timer.
   108  		// 1e9 ms == ~11.5 days.
   109  		wait = 1e9
   110  	}
   111  
   112  	n = uint32(len(entries) / int(gomaxprocs))
   113  	if n < 8 {
   114  		n = 8
   115  	}
   116  	if delay != 0 {
   117  		mp.blocked = true
   118  	}
   119  	if stdcall6(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
   120  		mp.blocked = false
   121  		errno = int32(getlasterror())
   122  		if errno == _WAIT_TIMEOUT {
   123  			return gList{}
   124  		}
   125  		println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
   126  		throw("runtime: netpoll failed")
   127  	}
   128  	mp.blocked = false
   129  	for i = 0; i < n; i++ {
   130  		op = entries[i].op
   131  		if op != nil {
   132  			errno = 0
   133  			qty = 0
   134  			if stdcall5(_WSAGetOverlappedResult, op.pd.fd, uintptr(unsafe.Pointer(op)), uintptr(unsafe.Pointer(&qty)), 0, uintptr(unsafe.Pointer(&flags))) == 0 {
   135  				errno = int32(getlasterror())
   136  			}
   137  			handlecompletion(&toRun, op, errno, qty)
   138  		} else {
   139  			netpollWakeSig.Store(0)
   140  			if delay == 0 {
   141  				// Forward the notification to the
   142  				// blocked poller.
   143  				netpollBreak()
   144  			}
   145  		}
   146  	}
   147  	return toRun
   148  }
   149  
   150  func handlecompletion(toRun *gList, op *net_op, errno int32, qty uint32) {
   151  	mode := op.mode
   152  	if mode != 'r' && mode != 'w' {
   153  		println("runtime: GetQueuedCompletionStatusEx returned invalid mode=", mode)
   154  		throw("runtime: netpoll failed")
   155  	}
   156  	op.errno = errno
   157  	op.qty = qty
   158  	netpollready(toRun, op.pd, mode)
   159  }
   160  

View as plain text