1// Copyright 2018 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 5package runtime 6 7import "unsafe" 8 9// This is based on the former libgo/runtime/netpoll_select.c implementation 10// except that it uses poll instead of select and is written in Go. 11// It's also based on Solaris implementation for the arming mechanisms 12 13//go:noescape 14//extern poll 15func libc_poll(pfds *pollfd, npfds uintptr, timeout uintptr) int32 16 17// pollfd represents the poll structure for AIX operating system. 18type pollfd struct { 19 fd int32 20 events int16 21 revents int16 22} 23 24const _POLLIN = 0x0001 25const _POLLOUT = 0x0002 26const _POLLHUP = 0x2000 27const _POLLERR = 0x4000 28 29var ( 30 pfds []pollfd 31 pds []*pollDesc 32 mtxpoll mutex 33 mtxset mutex 34 rdwake int32 35 wrwake int32 36 pendingUpdates int32 37) 38 39func netpollinit() { 40 // Create the pipe we use to wakeup poll. 41 r, w, errno := nonblockingPipe() 42 if errno != 0 { 43 throw("netpollinit: failed to create pipe") 44 } 45 rdwake = r 46 wrwake = w 47 48 // Pre-allocate array of pollfd structures for poll. 49 pfds = make([]pollfd, 1, 128) 50 51 // Poll the read side of the pipe. 52 pfds[0].fd = rdwake 53 pfds[0].events = _POLLIN 54 55 pds = make([]*pollDesc, 1, 128) 56 pds[0] = nil 57} 58 59func netpollIsPollDescriptor(fd uintptr) bool { 60 return fd == uintptr(rdwake) || fd == uintptr(wrwake) 61} 62 63// netpollwakeup writes on wrwake to wakeup poll before any changes. 64func netpollwakeup() { 65 if pendingUpdates == 0 { 66 pendingUpdates = 1 67 b := [1]byte{0} 68 write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) 69 } 70} 71 72func netpollopen(fd uintptr, pd *pollDesc) int32 { 73 lock(&mtxpoll) 74 netpollwakeup() 75 76 lock(&mtxset) 77 unlock(&mtxpoll) 78 79 pd.user = uint32(len(pfds)) 80 pfds = append(pfds, pollfd{fd: int32(fd)}) 81 pds = append(pds, pd) 82 unlock(&mtxset) 83 return 0 84} 85 86func netpollclose(fd uintptr) int32 { 87 lock(&mtxpoll) 88 netpollwakeup() 89 90 lock(&mtxset) 91 unlock(&mtxpoll) 92 93 for i := 0; i < len(pfds); i++ { 94 if pfds[i].fd == int32(fd) { 95 pfds[i] = pfds[len(pfds)-1] 96 pfds = pfds[:len(pfds)-1] 97 98 pds[i] = pds[len(pds)-1] 99 pds[i].user = uint32(i) 100 pds = pds[:len(pds)-1] 101 break 102 } 103 } 104 unlock(&mtxset) 105 return 0 106} 107 108func netpollarm(pd *pollDesc, mode int) { 109 lock(&mtxpoll) 110 netpollwakeup() 111 112 lock(&mtxset) 113 unlock(&mtxpoll) 114 115 switch mode { 116 case 'r': 117 pfds[pd.user].events |= _POLLIN 118 case 'w': 119 pfds[pd.user].events |= _POLLOUT 120 } 121 unlock(&mtxset) 122} 123 124// netpollBreak interrupts an epollwait. 125func netpollBreak() { 126 netpollwakeup() 127} 128 129// netpoll checks for ready network connections. 130// Returns list of goroutines that become runnable. 131// delay < 0: blocks indefinitely 132// delay == 0: does not block, just polls 133// delay > 0: block for up to that many nanoseconds 134//go:nowritebarrierrec 135func netpoll(delay int64) gList { 136 var timeout uintptr 137 if delay < 0 { 138 timeout = ^uintptr(0) 139 } else if delay == 0 { 140 // TODO: call poll with timeout == 0 141 return gList{} 142 } else if delay < 1e6 { 143 timeout = 1 144 } else if delay < 1e15 { 145 timeout = uintptr(delay / 1e6) 146 } else { 147 // An arbitrary cap on how long to wait for a timer. 148 // 1e9 ms == ~11.5 days. 149 timeout = 1e9 150 } 151retry: 152 lock(&mtxpoll) 153 lock(&mtxset) 154 pendingUpdates = 0 155 unlock(&mtxpoll) 156 157 n := libc_poll(&pfds[0], uintptr(len(pfds)), timeout) 158 if n < 0 { 159 e := errno() 160 if e != _EINTR { 161 println("errno=", e, " len(pfds)=", len(pfds)) 162 throw("poll failed") 163 } 164 unlock(&mtxset) 165 // If a timed sleep was interrupted, just return to 166 // recalculate how long we should sleep now. 167 if timeout > 0 { 168 return gList{} 169 } 170 goto retry 171 } 172 // Check if some descriptors need to be changed 173 if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 174 if delay != 0 { 175 // A netpollwakeup could be picked up by a 176 // non-blocking poll. Only clear the wakeup 177 // if blocking. 178 var b [1]byte 179 for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 { 180 } 181 } 182 // Still look at the other fds even if the mode may have 183 // changed, as netpollBreak might have been called. 184 n-- 185 } 186 var toRun gList 187 for i := 1; i < len(pfds) && n > 0; i++ { 188 pfd := &pfds[i] 189 190 var mode int32 191 if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 192 mode += 'r' 193 pfd.events &= ^_POLLIN 194 } 195 if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 { 196 mode += 'w' 197 pfd.events &= ^_POLLOUT 198 } 199 if mode != 0 { 200 pds[i].everr = false 201 if pfd.revents == _POLLERR { 202 pds[i].everr = true 203 } 204 netpollready(&toRun, pds[i], mode) 205 n-- 206 } 207 } 208 unlock(&mtxset) 209 return toRun 210} 211