1// Copyright 2019 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// FIXME: Improve network poller for hurd. 10// This is based on the former libgo/runtime/netpoll_select.c implementation 11// except that it uses poll instead of select and is written in Go. 12// It's also based on Solaris implementation for the arming mechanisms 13// Inspiration was also taken from netpoll_aix.go and netpoll_solaris.go 14 15//From /usr/include/x86_64-linux-gnu/sys/poll.h 16//go:noescape 17//extern poll 18func libc_poll(pfds *pollfd, nfds int32, timeout int32) int32 19 20//go:noescape 21//extern pipe2 22func libc_pipe2(fd *int32, flags int32) int32 23 24//pollfd represents the poll structure for GNU/Hurd operating system. 25type pollfd struct { 26 fd int32 // File descriptor to poll. 27 events int16 // Types of events poller cares about. 28 revents int16 // Types of events that actually occurred. 29} 30 31//From /usr/include/i386-gnu/bits/poll.h 32const _POLLIN = 01 // There is data to read. 33const _POLLPRI = 02 // There is urgent data to read. 34const _POLLOUT = 04 // Writing now will not block. 35const _POLLERR = 010 // Error condition. 36const _POLLHUP = 020 // Hung up. 37const _POLLNVAL = 040 // Invalid polling request. 38 39var ( 40 pfds []pollfd 41 pds []*pollDesc 42 mtxpoll mutex 43 mtxset mutex 44 rdwake int32 45 wrwake int32 46 pendingUpdates int32 47) 48 49const pollVerbose = false 50 51func netpollinit() { 52 var p [2]int32 53 54 // Create the pipe we use to wakeup poll. 55 if err := libc_pipe2(&p[0], _O_CLOEXEC|_O_NONBLOCK); err < 0 { 56 throw("runtime:netpollinit(): failed to create pipe2") 57 } 58 rdwake = p[0] 59 wrwake = p[1] 60 61 // Pre-allocate array of pollfd structures for poll. 62 if pollVerbose { 63 println("*** allocating") 64 } 65 pfds = make([]pollfd, 1, 128) 66 if pollVerbose { 67 println("*** allocating done", &pfds[0]) 68 } 69 70 // Poll the read side of the pipe. 71 pfds[0].fd = int32(rdwake) 72 pfds[0].events = int16(_POLLIN) 73 pfds[0].revents = int16(0) 74 75 pds = make([]*pollDesc, 1, 128) 76 // Checks for pd != nil are made in netpoll. 77 pds[0] = nil 78} 79 80func netpolldescriptor() uintptr { 81 // Both fds must be returned. 82 if rdwake > 0xFFFF || wrwake > 0xFFFF { 83 throw("netpolldescriptor: invalid fd number") 84 } 85 return uintptr(rdwake<<16 | wrwake) 86} 87 88// netpollwakeup writes on wrwake to wakeup poll before any changes. 89func netpollwakeup() { 90 if pendingUpdates == 0 { 91 pendingUpdates = 1 92 if pollVerbose { 93 println("*** writing 1 byte") 94 } 95 b := [1]byte{0} 96 write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) 97 } 98} 99 100func netpollopen(fd uintptr, pd *pollDesc) int32 { 101 if pollVerbose { 102 println("*** netpollopen", fd) 103 } 104 lock(&mtxpoll) 105 netpollwakeup() 106 107 lock(&mtxset) 108 unlock(&mtxpoll) 109 110 pd.user = uint32(len(pfds)) 111 pfds = append(pfds, pollfd{fd: int32(fd)}) 112 pds = append(pds, pd) 113 unlock(&mtxset) 114 return 0 115} 116 117func netpollclose(fd uintptr) int32 { 118 if pollVerbose { 119 println("*** netpollclose", fd) 120 } 121 lock(&mtxpoll) 122 netpollwakeup() 123 124 lock(&mtxset) 125 unlock(&mtxpoll) 126 127 for i := 0; i < len(pfds); i++ { 128 if pfds[i].fd == int32(fd) { 129 pfds[i] = pfds[len(pfds)-1] 130 pfds = pfds[:len(pfds)-1] 131 132 pds[i] = pds[len(pds)-1] 133 pds[i].user = uint32(i) 134 pds = pds[:len(pds)-1] 135 break 136 } 137 } 138 unlock(&mtxset) 139 return 0 140} 141 142func netpollarm(pd *pollDesc, mode int) { 143 if pollVerbose { 144 println("*** netpollarm", pd.fd, mode) 145 } 146 lock(&mtxpoll) 147 netpollwakeup() 148 149 lock(&mtxset) 150 unlock(&mtxpoll) 151 152 switch mode { 153 case 'r': 154 pfds[pd.user].events |= _POLLIN 155 case 'w': 156 pfds[pd.user].events |= _POLLOUT 157 } 158 unlock(&mtxset) 159} 160 161// polls for ready network connections 162// returns list of goroutines that become runnable 163//go:nowritebarrierrec 164func netpoll(block bool) gList { 165 timeout := int32(0) 166 if !block { 167 timeout = 0 168 return gList{} 169 } 170 if pollVerbose { 171 println("*** netpoll", block) 172 } 173retry: 174 lock(&mtxpoll) 175 lock(&mtxset) 176 pendingUpdates = 0 177 unlock(&mtxpoll) 178 179 if pollVerbose { 180 println("*** netpoll before poll") 181 } 182 n := libc_poll(&pfds[0], int32(len(pfds)), timeout) 183 if pollVerbose { 184 println("*** netpoll after poll", n) 185 } 186 if n < 0 { 187 e := errno() 188 if e != _EINTR { 189 println("errno=", e, " len(pfds)=", len(pfds)) 190 throw("poll failed") 191 } 192 if pollVerbose { 193 println("*** poll failed") 194 } 195 unlock(&mtxset) 196 goto retry 197 } 198 // Check if some descriptors need to be changed 199 if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 200 var b [1]byte 201 for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 { 202 if pollVerbose { 203 println("*** read 1 byte from pipe") 204 } 205 } 206 // Do not look at the other fds in this case as the mode may have changed 207 // XXX only additions of flags are made, so maybe it is ok 208 unlock(&mtxset) 209 goto retry 210 } 211 var toRun gList 212 for i := 0; i < len(pfds) && n > 0; i++ { 213 pfd := &pfds[i] 214 215 var mode int32 216 if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 217 mode += 'r' 218 pfd.events &= ^_POLLIN 219 } 220 if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 { 221 mode += 'w' 222 pfd.events &= ^_POLLOUT 223 } 224 if mode != 0 { 225 if pollVerbose { 226 println("*** netpollready i=", i, "revents=", pfd.revents, "events=", pfd.events, "pd=", pds[i]) 227 } 228 netpollready(&toRun, pds[i], mode) 229 n-- 230 } 231 } 232 unlock(&mtxset) 233 if block && toRun.empty() { 234 goto retry 235 } 236 if pollVerbose { 237 println("*** netpoll returning end") 238 } 239 return toRun 240} 241