runtime: add hurd netpoll and semaphore support

Patch by Svante Signell.

Change-Id: I6f6ba6cf988e75e43c3fd4fc0341b45e4866dbcd
Reviewed-on: https://go-review.googlesource.com/c/160827
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/libgo/go/runtime/netpoll.go b/libgo/go/runtime/netpoll.go
index e71ba08..6d39114 100644
--- a/libgo/go/runtime/netpoll.go
+++ b/libgo/go/runtime/netpoll.go
@@ -102,7 +102,7 @@
 // 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 @@
 	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 0000000..b74ad2f
--- /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 0000000..1282553
--- /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")
+	}
+}