From fc34dbfdb0371365321528c9a96f19827f959ec8 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 1 Feb 2019 23:57:08 +0000 Subject: [PATCH] runtime: add hurd netpoll and semaphore support Patch by Svante Signell. Reviewed-on: https://go-review.googlesource.com/c/160827 From-SVN: r268465 --- gcc/go/gofrontend/MERGE | 2 +- libgo/go/runtime/netpoll.go | 6 +- libgo/go/runtime/netpoll_hurd.go | 240 +++++++++++++++++++++++++++++++ libgo/go/runtime/os_hurd.go | 87 +++++++++++ 4 files changed, 331 insertions(+), 4 deletions(-) create mode 100644 libgo/go/runtime/netpoll_hurd.go create mode 100644 libgo/go/runtime/os_hurd.go diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index c32a85b2f77..3b43b63b5d2 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -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. diff --git a/libgo/go/runtime/netpoll.go b/libgo/go/runtime/netpoll.go index e71ba084da8..6d391146a44 100644 --- a/libgo/go/runtime/netpoll.go +++ b/libgo/go/runtime/netpoll.go @@ -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 index 00000000000..b74ad2fe128 --- /dev/null +++ b/libgo/go/runtime/netpoll_hurd.go @@ -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 index 00000000000..12825532a26 --- /dev/null +++ b/libgo/go/runtime/os_hurd.go @@ -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") + } +} -- 2.30.2