blob: e40dfb635263771b80426705a6b4283cacb46d2f [file] [log] [blame]
// 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()
}