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//go:noescape 18//extern pipe 19func libc_pipe(fd *int32) int32 20 21//extern __go_fcntl_uintptr 22func fcntlUintptr(fd, cmd, arg uintptr) (uintptr, uintptr) 23 24func fcntl(fd, cmd int32, arg uintptr) int32 { 25 r, _ := fcntlUintptr(uintptr(fd), uintptr(cmd), arg) 26 return int32(r) 27} 28 29// pollfd represents the poll structure for AIX operating system. 30type pollfd struct { 31 fd int32 32 events int16 33 revents int16 34} 35 36const _POLLIN = 0x0001 37const _POLLOUT = 0x0002 38const _POLLHUP = 0x2000 39const _POLLERR = 0x4000 40 41var ( 42 pfds []pollfd 43 pds []*pollDesc 44 mtxpoll mutex 45 mtxset mutex 46 rdwake int32 47 wrwake int32 48 pendingUpdates int32 49) 50 51const pollVerbose = false 52 53func netpollinit() { 54 var p [2]int32 55 56 // Create the pipe we use to wakeup poll. 57 if err := libc_pipe(&p[0]); err < 0 { 58 throw("netpollinit: failed to create pipe") 59 } 60 rdwake = p[0] 61 wrwake = p[1] 62 63 fl := uintptr(fcntl(rdwake, _F_GETFL, 0)) 64 fcntl(rdwake, _F_SETFL, fl|_O_NONBLOCK) 65 fcntl(rdwake, _F_SETFD, _FD_CLOEXEC) 66 67 fl = uintptr(fcntl(wrwake, _F_GETFL, 0)) 68 fcntl(wrwake, _F_SETFL, fl|_O_NONBLOCK) 69 fcntl(wrwake, _F_SETFD, _FD_CLOEXEC) 70 71 // Pre-allocate array of pollfd structures for poll. 72 if pollVerbose { 73 println("*** allocating") 74 } 75 pfds = make([]pollfd, 1, 128) 76 if pollVerbose { 77 println("*** allocating done", &pfds[0]) 78 } 79 80 // Poll the read side of the pipe. 81 pfds[0].fd = rdwake 82 pfds[0].events = _POLLIN 83 84 pds = make([]*pollDesc, 1, 128) 85 pds[0] = nil 86} 87 88func netpolldescriptor() uintptr { 89 // Both fd must be returned 90 if rdwake > 0xFFFF || wrwake > 0xFFFF { 91 throw("netpolldescriptor: invalid fd number") 92 } 93 return uintptr(rdwake<<16 | wrwake) 94} 95 96// netpollwakeup writes on wrwake to wakeup poll before any changes. 97func netpollwakeup() { 98 if pendingUpdates == 0 { 99 pendingUpdates = 1 100 if pollVerbose { 101 println("*** writing 1 byte") 102 } 103 b := [1]byte{0} 104 write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) 105 } 106} 107 108func netpollopen(fd uintptr, pd *pollDesc) int32 { 109 if pollVerbose { 110 println("*** netpollopen", fd) 111 } 112 lock(&mtxpoll) 113 netpollwakeup() 114 115 lock(&mtxset) 116 unlock(&mtxpoll) 117 118 pd.user = uint32(len(pfds)) 119 pfds = append(pfds, pollfd{fd: int32(fd)}) 120 pds = append(pds, pd) 121 unlock(&mtxset) 122 return 0 123} 124 125func netpollclose(fd uintptr) int32 { 126 if pollVerbose { 127 println("*** netpollclose", fd) 128 } 129 lock(&mtxpoll) 130 netpollwakeup() 131 132 lock(&mtxset) 133 unlock(&mtxpoll) 134 135 for i := 0; i < len(pfds); i++ { 136 if pfds[i].fd == int32(fd) { 137 pfds[i] = pfds[len(pfds)-1] 138 pfds = pfds[:len(pfds)-1] 139 140 pds[i] = pds[len(pds)-1] 141 pds[i].user = uint32(i) 142 pds = pds[:len(pds)-1] 143 break 144 } 145 } 146 unlock(&mtxset) 147 return 0 148} 149 150func netpollarm(pd *pollDesc, mode int) { 151 if pollVerbose { 152 println("*** netpollarm", pd.fd, mode) 153 } 154 lock(&mtxpoll) 155 netpollwakeup() 156 157 lock(&mtxset) 158 unlock(&mtxpoll) 159 160 switch mode { 161 case 'r': 162 pfds[pd.user].events |= _POLLIN 163 case 'w': 164 pfds[pd.user].events |= _POLLOUT 165 } 166 unlock(&mtxset) 167} 168 169//go:nowritebarrierrec 170func netpoll(block bool) gList { 171 timeout := ^uintptr(0) 172 if !block { 173 timeout = 0 174 return gList{} 175 } 176 if pollVerbose { 177 println("*** netpoll", block) 178 } 179retry: 180 lock(&mtxpoll) 181 lock(&mtxset) 182 pendingUpdates = 0 183 unlock(&mtxpoll) 184 185 if pollVerbose { 186 println("*** netpoll before poll") 187 } 188 n := libc_poll(&pfds[0], uintptr(len(pfds)), timeout) 189 if pollVerbose { 190 println("*** netpoll after poll", n) 191 } 192 if n < 0 { 193 e := errno() 194 if e != _EINTR { 195 println("errno=", e, " len(pfds)=", len(pfds)) 196 throw("poll failed") 197 } 198 if pollVerbose { 199 println("*** poll failed") 200 } 201 unlock(&mtxset) 202 goto retry 203 } 204 // Check if some descriptors need to be changed 205 if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 206 var b [1]byte 207 for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 { 208 if pollVerbose { 209 println("*** read 1 byte from pipe") 210 } 211 } 212 // Do not look at the other fds in this case as the mode may have changed 213 // XXX only additions of flags are made, so maybe it is ok 214 unlock(&mtxset) 215 goto retry 216 } 217 var toRun gList 218 for i := 0; i < len(pfds) && n > 0; i++ { 219 pfd := &pfds[i] 220 221 var mode int32 222 if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 223 mode += 'r' 224 pfd.events &= ^_POLLIN 225 } 226 if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 { 227 mode += 'w' 228 pfd.events &= ^_POLLOUT 229 } 230 if mode != 0 { 231 if pollVerbose { 232 println("*** netpollready i=", i, "revents=", pfd.revents, "events=", pfd.events, "pd=", pds[i]) 233 } 234 netpollready(&toRun, pds[i], mode) 235 n-- 236 } 237 } 238 unlock(&mtxset) 239 if block && toRun.empty() { 240 goto retry 241 } 242 if pollVerbose { 243 println("*** netpoll returning end") 244 } 245 return toRun 246} 247