runtime: add hurd netpoll and semaphore support
authorIan Lance Taylor <ian@gcc.gnu.org>
Fri, 1 Feb 2019 23:57:08 +0000 (23:57 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Fri, 1 Feb 2019 23:57:08 +0000 (23:57 +0000)
    Patch by Svante Signell.

    Reviewed-on: https://go-review.googlesource.com/c/160827

From-SVN: r268465

gcc/go/gofrontend/MERGE
libgo/go/runtime/netpoll.go
libgo/go/runtime/netpoll_hurd.go [new file with mode: 0644]
libgo/go/runtime/os_hurd.go [new file with mode: 0644]

index c32a85b2f774a3dab0a2671211c80a2a89c417e2..3b43b63b5d2a822d56b3dae5bd7ebd5ac94f85e0 100644 (file)
@@ -1,4 +1,4 @@
-582392b80c07bd7e830e177b775dc4ef802b5fd6
+047b0aa6a29d46fde99b3e5823339ac8866f797c
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index e71ba084da8a75562d30fbe711c3a11bdf1e16fc..6d391146a44ed88149a6af4041c19445d3baa55d 100644 (file)
@@ -102,7 +102,7 @@ func netpollinited() bool {
 // descriptor being used by netpoll.
 func poll_runtime_isPollServerDescriptor(fd uintptr) bool {
        fds := netpolldescriptor()
-       if GOOS != "aix" {
+       if GOOS != "aix" && GOOS != "hurd" {
                return fd == fds
        } else {
                // AIX have a pipe in its netpoll implementation.
@@ -178,8 +178,8 @@ func poll_runtime_pollWait(pd *pollDesc, mode int) int {
        if err != 0 {
                return err
        }
-       // As for now only Solaris and AIX use level-triggered IO.
-       if GOOS == "solaris" || GOOS == "aix" {
+       // As for now only Solaris, AIX and Hurd use level-triggered IO.
+       if GOOS == "solaris" || GOOS == "aix" || GOOS == "hurd" {
                netpollarm(pd, mode)
        }
        for !netpollblock(pd, int32(mode), false) {
diff --git a/libgo/go/runtime/netpoll_hurd.go b/libgo/go/runtime/netpoll_hurd.go
new file mode 100644 (file)
index 0000000..b74ad2f
--- /dev/null
@@ -0,0 +1,240 @@
+// Copyright 2019 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"
+
+// FIXME: Improve network poller for hurd.
+// 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.
+// It's also based on Solaris implementation for the arming mechanisms
+// Inspiration was also taken from netpoll_aix.go and netpoll_solaris.go
+
+//From /usr/include/x86_64-linux-gnu/sys/poll.h
+//go:noescape
+//extern poll
+func libc_poll(pfds *pollfd, nfds int32, timeout int32) int32
+
+//go:noescape
+//extern pipe2
+func libc_pipe2(fd *int32, flags int32) int32
+
+//pollfd represents the poll structure for GNU/Hurd operating system.
+type pollfd struct {
+       fd      int32 // File descriptor to poll.
+       events  int16 // Types of events poller cares about.
+       revents int16 // Types of events that actually occurred.
+}
+
+//From /usr/include/i386-gnu/bits/poll.h
+const _POLLIN = 01    // There is data to read.
+const _POLLPRI = 02   // There is urgent data to read.
+const _POLLOUT = 04   // Writing now will not block.
+const _POLLERR = 010  // Error condition.
+const _POLLHUP = 020  // Hung up.
+const _POLLNVAL = 040 // Invalid polling request.
+
+var (
+       pfds           []pollfd
+       pds            []*pollDesc
+       mtxpoll        mutex
+       mtxset         mutex
+       rdwake         int32
+       wrwake         int32
+       pendingUpdates int32
+)
+
+const pollVerbose = false
+
+func netpollinit() {
+       var p [2]int32
+
+       // Create the pipe we use to wakeup poll.
+       if err := libc_pipe2(&p[0], _O_CLOEXEC|_O_NONBLOCK); err < 0 {
+               throw("runtime:netpollinit(): failed to create pipe2")
+       }
+       rdwake = p[0]
+       wrwake = p[1]
+
+       // Pre-allocate array of pollfd structures for poll.
+       if pollVerbose {
+               println("*** allocating")
+       }
+       pfds = make([]pollfd, 1, 128)
+       if pollVerbose {
+               println("*** allocating done", &pfds[0])
+       }
+
+       // Poll the read side of the pipe.
+       pfds[0].fd = int32(rdwake)
+       pfds[0].events = int16(_POLLIN)
+       pfds[0].revents = int16(0)
+
+       pds = make([]*pollDesc, 1, 128)
+       // Checks for pd != nil are made in netpoll.
+       pds[0] = nil
+}
+
+func netpolldescriptor() uintptr {
+       // Both fds must be returned.
+       if rdwake > 0xFFFF || wrwake > 0xFFFF {
+               throw("netpolldescriptor: invalid fd number")
+       }
+       return uintptr(rdwake<<16 | wrwake)
+}
+
+// netpollwakeup writes on wrwake to wakeup poll before any changes.
+func netpollwakeup() {
+       if pendingUpdates == 0 {
+               pendingUpdates = 1
+               if pollVerbose {
+                       println("*** writing 1 byte")
+               }
+               b := [1]byte{0}
+               write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1)
+       }
+}
+
+func netpollopen(fd uintptr, pd *pollDesc) int32 {
+       if pollVerbose {
+               println("*** netpollopen", fd)
+       }
+       lock(&mtxpoll)
+       netpollwakeup()
+
+       lock(&mtxset)
+       unlock(&mtxpoll)
+
+       pd.user = uint32(len(pfds))
+       pfds = append(pfds, pollfd{fd: int32(fd)})
+       pds = append(pds, pd)
+       unlock(&mtxset)
+       return 0
+}
+
+func netpollclose(fd uintptr) int32 {
+       if pollVerbose {
+               println("*** netpollclose", fd)
+       }
+       lock(&mtxpoll)
+       netpollwakeup()
+
+       lock(&mtxset)
+       unlock(&mtxpoll)
+
+       for i := 0; i < len(pfds); i++ {
+               if pfds[i].fd == int32(fd) {
+                       pfds[i] = pfds[len(pfds)-1]
+                       pfds = pfds[:len(pfds)-1]
+
+                       pds[i] = pds[len(pds)-1]
+                       pds[i].user = uint32(i)
+                       pds = pds[:len(pds)-1]
+                       break
+               }
+       }
+       unlock(&mtxset)
+       return 0
+}
+
+func netpollarm(pd *pollDesc, mode int) {
+       if pollVerbose {
+               println("*** netpollarm", pd.fd, mode)
+       }
+       lock(&mtxpoll)
+       netpollwakeup()
+
+       lock(&mtxset)
+       unlock(&mtxpoll)
+
+       switch mode {
+       case 'r':
+               pfds[pd.user].events |= _POLLIN
+       case 'w':
+               pfds[pd.user].events |= _POLLOUT
+       }
+       unlock(&mtxset)
+}
+
+// polls for ready network connections
+// returns list of goroutines that become runnable
+//go:nowritebarrierrec
+func netpoll(block bool) gList {
+       timeout := int32(0)
+       if !block {
+               timeout = 0
+               return gList{}
+       }
+       if pollVerbose {
+               println("*** netpoll", block)
+       }
+retry:
+       lock(&mtxpoll)
+       lock(&mtxset)
+       pendingUpdates = 0
+       unlock(&mtxpoll)
+
+       if pollVerbose {
+               println("*** netpoll before poll")
+       }
+       n := libc_poll(&pfds[0], int32(len(pfds)), timeout)
+       if pollVerbose {
+               println("*** netpoll after poll", n)
+       }
+       if n < 0 {
+               e := errno()
+               if e != _EINTR {
+                       println("errno=", e, " len(pfds)=", len(pfds))
+                       throw("poll failed")
+               }
+               if pollVerbose {
+                       println("*** poll failed")
+               }
+               unlock(&mtxset)
+               goto retry
+       }
+       // Check if some descriptors need to be changed
+       if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
+               var b [1]byte
+               for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 {
+                       if pollVerbose {
+                               println("*** read 1 byte from pipe")
+                       }
+               }
+               // Do not look at the other fds in this case as the mode may have changed
+               // XXX only additions of flags are made, so maybe it is ok
+               unlock(&mtxset)
+               goto retry
+       }
+       var toRun gList
+       for i := 0; i < len(pfds) && n > 0; i++ {
+               pfd := &pfds[i]
+
+               var mode int32
+               if pfd.revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 {
+                       mode += 'r'
+                       pfd.events &= ^_POLLIN
+               }
+               if pfd.revents&(_POLLOUT|_POLLHUP|_POLLERR) != 0 {
+                       mode += 'w'
+                       pfd.events &= ^_POLLOUT
+               }
+               if mode != 0 {
+                       if pollVerbose {
+                               println("*** netpollready i=", i, "revents=", pfd.revents, "events=", pfd.events, "pd=", pds[i])
+                       }
+                       netpollready(&toRun, pds[i], mode)
+                       n--
+               }
+       }
+       unlock(&mtxset)
+       if block && toRun.empty() {
+               goto retry
+       }
+       if pollVerbose {
+               println("*** netpoll returning end")
+       }
+       return toRun
+}
diff --git a/libgo/go/runtime/os_hurd.go b/libgo/go/runtime/os_hurd.go
new file mode 100644 (file)
index 0000000..1282553
--- /dev/null
@@ -0,0 +1,87 @@
+// Copyright 2019 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.
+
+// This file is derived from os_solaris.go.
+
+package runtime
+
+import "unsafe"
+
+type mOS struct {
+       waitsema uintptr // semaphore for parking on locks
+}
+
+//extern malloc
+func libc_malloc(uintptr) unsafe.Pointer
+
+//go:noescape
+//extern sem_init
+func sem_init(sem *_sem_t, pshared int32, value uint32) int32
+
+//go:noescape
+//extern sem_wait
+func sem_wait(sem *_sem_t) int32
+
+//go:noescape
+//extern sem_post
+func sem_post(sem *_sem_t) int32
+
+//go:noescape
+//extern sem_timedwait
+func sem_timedwait(sem *_sem_t, timeout *timespec) int32
+
+//go:nosplit
+func semacreate(mp *m) {
+       if mp.mos.waitsema != 0 {
+               return
+       }
+
+       var sem *_sem_t
+
+       // Call libc's malloc rather than malloc. This will
+       // allocate space on the C heap. We can't call malloc
+       // here because it could cause a deadlock.
+       sem = (*_sem_t)(libc_malloc(unsafe.Sizeof(*sem)))
+       if sem_init(sem, 0, 0) != 0 {
+               throw("sem_init")
+       }
+       mp.mos.waitsema = uintptr(unsafe.Pointer(sem))
+}
+
+//go:nosplit
+func semasleep(ns int64) int32 {
+       _m_ := getg().m
+       if ns >= 0 {
+               var ts timespec
+               ts.set_sec(ns / 1000000000)
+               ts.set_nsec(int32(ns % 1000000000))
+
+               if sem_timedwait((*_sem_t)(unsafe.Pointer(_m_.mos.waitsema)), &ts) != 0 {
+                       err := errno()
+                       if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR {
+                               return -1
+                       }
+                       throw("sem_timedwait")
+               }
+               return 0
+       }
+       for {
+               r1 := sem_wait((*_sem_t)(unsafe.Pointer(_m_.mos.waitsema)))
+               if r1 == 0 {
+                       break
+               }
+               if errno() == _EINTR {
+                       continue
+               }
+               throw("sem_wait")
+       }
+       return 0
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+       if sem_post((*_sem_t)(unsafe.Pointer(mp.mos.waitsema))) != 0 {
+               throw("sem_post")
+       }
+}