| // Copyright 2017 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package runtime |
| |
| import "unsafe" |
| |
| // This is based on the former libgo/runtime/netpoll_select.c implementation |
| // except that it uses poll instead of select and is written in Go. |
| |
| // These definitions should come from sysinfo.go as they may be OS-dependent. |
| // These are the definitions for the AIX operating system. |
| type pollfd struct { |
| fd int32 |
| events int16 |
| revents int16 |
| } |
| |
| const _POLLIN = 0x0001 |
| const _POLLOUT = 0x0002 |
| const _POLLHUP = 0x2000 |
| const _POLLERR = 0x4000 |
| |
| //extern poll |
| func libc_poll(pfds *pollfd, npfds uintptr, timeout uintptr) int32 |
| |
| //extern pipe |
| func libc_pipe(fd *int32) int32 |
| |
| //extern __go_fcntl_uintptr |
| func fcntlUintptr(fd, cmd, arg uintptr) (uintptr, uintptr) |
| |
| func closeonexec(fd int32) { |
| fcntlUintptr(uintptr(fd), _F_SETFD, _FD_CLOEXEC) |
| } |
| |
| var ( |
| allocated int |
| pfds []pollfd |
| mpfds map[uintptr]*pollDesc |
| pmtx mutex |
| rdwake int32 |
| wrwake int32 |
| ) |
| |
| func netpollinit() { |
| var p [2]int32 |
| |
| // Create the pipe we use to wakeup poll. |
| if err := libc_pipe(&p[0]); err < 0 { |
| throw("netpollinit: failed to create pipe") |
| } |
| rdwake = p[0] |
| wrwake = p[1] |
| |
| closeonexec(rdwake) |
| closeonexec(wrwake) |
| |
| // Pre-allocate array of pollfd structures for poll. |
| allocated = 128 |
| pfds = make([]pollfd, allocated) |
| |
| mpfds = make(map[uintptr]*pollDesc) |
| } |
| |
| func netpollopen(fd uintptr, pd *pollDesc) int32 { |
| lock(&pmtx) |
| mpfds[fd] = pd |
| unlock(&pmtx) |
| |
| // Wakeup poll. |
| b := [1]byte{0} |
| write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) |
| |
| return 0 |
| } |
| |
| func netpollclose(fd uintptr) int32 { |
| lock(&pmtx) |
| delete(mpfds, fd) |
| unlock(&pmtx) |
| |
| // Wakeup poll. |
| b := [1]byte{0} |
| write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) |
| |
| return 0 |
| } |
| |
| func netpollarm(pd *pollDesc, mode int) { |
| throw("unused") |
| } |
| |
| func netpoll(block bool) *g { |
| if allocated == 0 { |
| return nil |
| } |
| timeout := ^uintptr(0) |
| if !block { |
| timeout = 0 |
| } |
| retry: |
| lock(&pmtx) |
| npfds := len(mpfds) + 1 |
| unlock(&pmtx) |
| |
| if npfds > allocated { |
| for npfds > allocated { |
| allocated *= 2 |
| } |
| pfds = make([]pollfd, allocated) |
| } |
| |
| // Poll the read side of the pipe. |
| pfds[0].fd = rdwake |
| pfds[0].events = _POLLIN |
| lock(&pmtx) |
| // Notice that npfds may have changed since we released the lock. |
| // Just copy what we can, new descriptors will be added at next |
| // iteration. |
| i := 1 |
| for fd := range mpfds { |
| if i >= allocated { |
| break |
| } |
| pfds[i].fd = int32(fd) |
| pfds[i].events = _POLLIN | _POLLOUT |
| i++ |
| } |
| npfds = i |
| unlock(&pmtx) |
| |
| n := libc_poll(&pfds[0], uintptr(npfds), timeout) |
| if n < 0 { |
| e := errno() |
| if e != _EINTR { |
| throw("poll failed") |
| } |
| goto retry |
| } |
| var gp guintptr |
| for i = 0; i < npfds && n > 0; i++ { |
| pfd := pfds[i] |
| |
| var mode int32 |
| if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { |
| if i == 0 { |
| var b [1]byte |
| read(pfd.fd, unsafe.Pointer(&b[0]), 1) |
| n-- |
| continue |
| } |
| mode += 'r' |
| } |
| if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 { |
| mode += 'w' |
| } |
| if mode != 0 { |
| lock(&pmtx) |
| pd := mpfds[uintptr(pfd.fd)] |
| unlock(&pmtx) |
| if pd != nil { |
| netpollready(&gp, pd, mode) |
| } |
| n-- |
| } |
| } |
| if block && gp == 0 { |
| goto retry |
| } |
| return gp.ptr() |
| } |