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

View as plain text