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