Commit fc34dbfd by Ian Lance Taylor

runtime: add hurd netpoll and semaphore support

    
    Patch by Svante Signell.
    
    Reviewed-on: https://go-review.googlesource.com/c/160827

From-SVN: r268465
parent 73353297
582392b80c07bd7e830e177b775dc4ef802b5fd6 047b0aa6a29d46fde99b3e5823339ac8866f797c
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -102,7 +102,7 @@ func netpollinited() bool { ...@@ -102,7 +102,7 @@ func netpollinited() bool {
// descriptor being used by netpoll. // descriptor being used by netpoll.
func poll_runtime_isPollServerDescriptor(fd uintptr) bool { func poll_runtime_isPollServerDescriptor(fd uintptr) bool {
fds := netpolldescriptor() fds := netpolldescriptor()
if GOOS != "aix" { if GOOS != "aix" && GOOS != "hurd" {
return fd == fds return fd == fds
} else { } else {
// AIX have a pipe in its netpoll implementation. // AIX have a pipe in its netpoll implementation.
...@@ -178,8 +178,8 @@ func poll_runtime_pollWait(pd *pollDesc, mode int) int { ...@@ -178,8 +178,8 @@ func poll_runtime_pollWait(pd *pollDesc, mode int) int {
if err != 0 { if err != 0 {
return err return err
} }
// As for now only Solaris and AIX use level-triggered IO. // As for now only Solaris, AIX and Hurd use level-triggered IO.
if GOOS == "solaris" || GOOS == "aix" { if GOOS == "solaris" || GOOS == "aix" || GOOS == "hurd" {
netpollarm(pd, mode) netpollarm(pd, mode)
} }
for !netpollblock(pd, int32(mode), false) { for !netpollblock(pd, int32(mode), false) {
......
// 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
}
// 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")
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment