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 88func netpollIsPollDescriptor(fd uintptr) bool { 89 return fd == uintptr(rdwake) || fd == uintptr(wrwake) 90} 91 92// netpollwakeup writes on wrwake to wakeup poll before any changes. 93func netpollwakeup() { 94 if pendingUpdates == 0 { 95 pendingUpdates = 1 96 if pollVerbose { 97 println("*** writing 1 byte") 98 } 99 b := [1]byte{0} 100 write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) 101 } 102} 103 104func netpollopen(fd uintptr, pd *pollDesc) int32 { 105 if pollVerbose { 106 println("*** netpollopen", fd) 107 } 108 lock(&mtxpoll) 109 netpollwakeup() 110 111 lock(&mtxset) 112 unlock(&mtxpoll) 113 114 pd.user = uint32(len(pfds)) 115 pfds = append(pfds, pollfd{fd: int32(fd)}) 116 pds = append(pds, pd) 117 unlock(&mtxset) 118 return 0 119} 120 121func netpollclose(fd uintptr) int32 { 122 if pollVerbose { 123 println("*** netpollclose", fd) 124 } 125 lock(&mtxpoll) 126 netpollwakeup() 127 128 lock(&mtxset) 129 unlock(&mtxpoll) 130 131 for i := 0; i < len(pfds); i++ { 132 if pfds[i].fd == int32(fd) { 133 pfds[i] = pfds[len(pfds)-1] 134 pfds = pfds[:len(pfds)-1] 135 136 pds[i] = pds[len(pds)-1] 137 pds[i].user = uint32(i) 138 pds = pds[:len(pds)-1] 139 break 140 } 141 } 142 unlock(&mtxset) 143 return 0 144} 145 146func netpollarm(pd *pollDesc, mode int) { 147 if pollVerbose { 148 println("*** netpollarm", pd.fd, mode) 149 } 150 lock(&mtxpoll) 151 netpollwakeup() 152 153 lock(&mtxset) 154 unlock(&mtxpoll) 155 156 switch mode { 157 case 'r': 158 pfds[pd.user].events |= _POLLIN 159 case 'w': 160 pfds[pd.user].events |= _POLLOUT 161 } 162 unlock(&mtxset) 163} 164 165// netpollBreak interrupts an epollwait. 166func netpollBreak() { 167 netpollwakeup() 168} 169 170// netpoll checks for ready network connections. 171// Returns list of goroutines that become runnable. 172// delay < 0: blocks indefinitely 173// delay == 0: does not block, just polls 174// delay > 0: block for up to that many nanoseconds 175//go:nowritebarrierrec 176func netpoll(delay int64) gList { 177 timeout := int32(0) 178 if delay < 0 { 179 timeout = 0 180 } else if delay == 0 { 181 // TODO: call poll with timeout == 0 182 return gList{} 183 } else if delay < 1e6 { 184 timeout = 1 185 } else if delay < 1e15 { 186 timeout = int32(delay / 1e6) 187 } else { 188 // An arbitrary cap on how long to wait for a timer. 189 // 1e9 ms == ~11.5 days. 190 timeout = 1e9 191 } 192retry: 193 lock(&mtxpoll) 194 lock(&mtxset) 195 pendingUpdates = 0 196 unlock(&mtxpoll) 197 198 n := libc_poll(&pfds[0], int32(len(pfds)), timeout) 199 if n < 0 { 200 e := errno() 201 if e != _EINTR { 202 println("errno=", e, " len(pfds)=", len(pfds)) 203 throw("poll failed") 204 } 205 unlock(&mtxset) 206 // If a timed sleep was interrupted, just return to 207 // recalculate how long we should sleep now. 208 if timeout > 0 { 209 return gList{} 210 } 211 goto retry 212 } 213 // Check if some descriptors need to be changed 214 if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 215 if delay != 0 { 216 // A netpollwakeup could be picked up by a 217 // non-blocking poll. Only clear the wakeup 218 // if blocking. 219 var b [1]byte 220 for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 { 221 } 222 } 223 // Still look at the other fds even if the mode may have 224 // changed, as netpollBreak might have been called. 225 n-- 226 } 227 var toRun gList 228 for i := 1; i < len(pfds) && n > 0; i++ { 229 pfd := &pfds[i] 230 231 var mode int32 232 if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { 233 mode += 'r' 234 pfd.events &= ^_POLLIN 235 } 236 if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 { 237 mode += 'w' 238 pfd.events &= ^_POLLOUT 239 } 240 if mode != 0 { 241 pds[i].everr = false 242 if pfd.revents == _POLLERR { 243 pds[i].everr = true 244 } 245 netpollready(&toRun, pds[i], mode) 246 n-- 247 } 248 } 249 unlock(&mtxset) 250 return toRun 251} 252