runtime: netpoll implementation for AIX

Code courtesy of Damien Bergamini from Atos Infogérance.

Issue golang/go#19200

Change-Id: I1f6f68c48cc4ebde82cd4b32440cffa0cd68c2eb
Reviewed-on: https://go-review.googlesource.com/40352
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/libgo/go/runtime/netpoll_aix.go b/libgo/go/runtime/netpoll_aix.go
index 51144e0..e40dfb6 100644
--- a/libgo/go/runtime/netpoll_aix.go
+++ b/libgo/go/runtime/netpoll_aix.go
@@ -2,25 +2,172 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Fake network poller for AIX
-// TODO : this is just stubs to allow building, implementation required
-
 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 {
-	return nil
+	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()
 }