runtime: better implementation of netpoll for AIX
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 16 Aug 2017 20:58:26 +0000 (20:58 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 16 Aug 2017 20:58:26 +0000 (20:58 +0000)
    Reviewed-on: https://go-review.googlesource.com/54170

From-SVN: r251133

gcc/go/gofrontend/MERGE
libgo/go/runtime/netpoll_aix.go

index 52fe0646c9da15de52fdaaf525b68157109b4377..3490c562fcc4127370d8c095a74f026db56f358a 100644 (file)
@@ -1,4 +1,4 @@
-f02183eb66f5718769f3f6541dcc6744ae1771c0
+152164a7249ecc5c2bfd4a091450dc7c2855f609
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index e40dfb635263771b80426705a6b4283cacb46d2f..8c8e1882219f2cb87aac7ca561b932791aeb5c5a 100644 (file)
@@ -7,10 +7,10 @@ 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.
+// except that it uses AIX pollset_poll instead of select and is written in Go.
+
+type pollset_t int32
 
-// 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
@@ -22,8 +22,23 @@ const _POLLOUT = 0x0002
 const _POLLHUP = 0x2000
 const _POLLERR = 0x4000
 
-//extern poll
-func libc_poll(pfds *pollfd, npfds uintptr, timeout uintptr) int32
+type poll_ctl struct {
+       cmd    int16
+       events int16
+       fd     int32
+}
+
+const _PS_ADD = 0x0
+const _PS_DELETE = 0x2
+
+//extern pollset_create
+func pollset_create(maxfd int32) pollset_t
+
+//extern pollset_ctl
+func pollset_ctl(ps pollset_t, pollctl_array *poll_ctl, array_length int32) int32
+
+//extern pollset_poll
+func pollset_poll(ps pollset_t, polldata_array *pollfd, array_length int32, timeout int32) int32
 
 //extern pipe
 func libc_pipe(fd *int32) int32
@@ -31,60 +46,100 @@ 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)
+func fcntl(fd, cmd int32, arg uintptr) uintptr {
+       r, _ := fcntlUintptr(uintptr(fd), uintptr(cmd), arg)
+       return r
 }
 
 var (
-       allocated int
-       pfds      []pollfd
-       mpfds     map[uintptr]*pollDesc
-       pmtx      mutex
-       rdwake    int32
-       wrwake    int32
+       ps          pollset_t = -1
+       mpfds       map[int32]*pollDesc
+       pmtx        mutex
+       rdwake      int32
+       wrwake      int32
+       needsUpdate bool
 )
 
 func netpollinit() {
        var p [2]int32
 
-       // Create the pipe we use to wakeup poll.
+       if ps = pollset_create(-1); ps < 0 {
+               throw("netpollinit: failed to create pollset")
+       }
+       // It is not possible to add or remove descriptors from
+       // the pollset while pollset_poll is active.
+       // We use a pipe to wakeup pollset_poll when the pollset
+       // needs to be updated.
        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)
+       fl := fcntl(rdwake, _F_GETFL, 0)
+       fcntl(rdwake, _F_SETFL, fl|_O_NONBLOCK)
+       fcntl(rdwake, _F_SETFD, _FD_CLOEXEC)
+
+       fl = fcntl(wrwake, _F_GETFL, 0)
+       fcntl(wrwake, _F_SETFL, fl|_O_NONBLOCK)
+       fcntl(wrwake, _F_SETFD, _FD_CLOEXEC)
+
+       // Add the read side of the pipe to the pollset.
+       var pctl poll_ctl
+       pctl.cmd = _PS_ADD
+       pctl.fd = rdwake
+       pctl.events = _POLLIN
+       if pollset_ctl(ps, &pctl, 1) != 0 {
+               throw("netpollinit: failed to register pipe")
+       }
 
-       mpfds = make(map[uintptr]*pollDesc)
+       mpfds = make(map[int32]*pollDesc)
 }
 
 func netpollopen(fd uintptr, pd *pollDesc) int32 {
+       // pollset_ctl will block if pollset_poll is active
+       // so wakeup pollset_poll first.
        lock(&pmtx)
-       mpfds[fd] = pd
+       needsUpdate = true
        unlock(&pmtx)
-
-       // Wakeup poll.
        b := [1]byte{0}
        write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
 
+       var pctl poll_ctl
+       pctl.cmd = _PS_ADD
+       pctl.fd = int32(fd)
+       pctl.events = _POLLIN | _POLLOUT
+       if pollset_ctl(ps, &pctl, 1) != 0 {
+               return int32(errno())
+       }
+       lock(&pmtx)
+       mpfds[int32(fd)] = pd
+       needsUpdate = false
+       unlock(&pmtx)
+
        return 0
 }
 
 func netpollclose(fd uintptr) int32 {
+       // pollset_ctl will block if pollset_poll is active
+       // so wakeup pollset_poll first.
        lock(&pmtx)
-       delete(mpfds, fd)
+       needsUpdate = true
        unlock(&pmtx)
-
-       // Wakeup poll.
        b := [1]byte{0}
        write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
 
+       var pctl poll_ctl
+       pctl.cmd = _PS_DELETE
+       pctl.fd = int32(fd)
+       if pollset_ctl(ps, &pctl, 1) != 0 {
+               return int32(errno())
+       }
+       lock(&pmtx)
+       delete(mpfds, int32(fd))
+       needsUpdate = false
+       unlock(&pmtx)
+
        return 0
 }
 
@@ -93,62 +148,39 @@ func netpollarm(pd *pollDesc, mode int) {
 }
 
 func netpoll(block bool) *g {
-       if allocated == 0 {
+       if ps == -1 {
                return nil
        }
-       timeout := ^uintptr(0)
+       timeout := int32(-1)
        if !block {
                timeout = 0
        }
+       var pfds [128]pollfd
 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++
+       if needsUpdate {
+               unlock(&pmtx)
+               osyield()
+               goto retry
        }
-       npfds = i
        unlock(&pmtx)
-
-       n := libc_poll(&pfds[0], uintptr(npfds), timeout)
-       if n < 0 {
+       nfound := pollset_poll(ps, &pfds[0], int32(len(pfds)), timeout)
+       if nfound < 0 {
                e := errno()
                if e != _EINTR {
-                       throw("poll failed")
+                       throw("pollset_poll failed")
                }
                goto retry
        }
        var gp guintptr
-       for i = 0; i < npfds && n > 0; i++ {
-               pfd := pfds[i]
+       for i := int32(0); i < nfound; i++ {
+               pfd := &pfds[i]
 
                var mode int32
                if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
-                       if i == 0 {
+                       if pfd.fd == rdwake {
                                var b [1]byte
                                read(pfd.fd, unsafe.Pointer(&b[0]), 1)
-                               n--
                                continue
                        }
                        mode += 'r'
@@ -158,12 +190,11 @@ retry:
                }
                if mode != 0 {
                        lock(&pmtx)
-                       pd := mpfds[uintptr(pfd.fd)]
+                       pd := mpfds[pfd.fd]
                        unlock(&pmtx)
                        if pd != nil {
                                netpollready(&gp, pd, mode)
                        }
-                       n--
                }
        }
        if block && gp == 0 {