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()
}