| // 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) |
| } |
| |
| func netpollIsPollDescriptor(fd uintptr) bool { |
| return fd == uintptr(rdwake) || fd == uintptr(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) |
| } |
| |
| // netpollBreak interrupts an epollwait. |
| func netpollBreak() { |
| netpollwakeup() |
| } |
| |
| // netpoll checks for ready network connections. |
| // Returns list of goroutines that become runnable. |
| // delay < 0: blocks indefinitely |
| // delay == 0: does not block, just polls |
| // delay > 0: block for up to that many nanoseconds |
| //go:nowritebarrierrec |
| func netpoll(delay int64) gList { |
| timeout := int32(0) |
| if delay < 0 { |
| timeout = 0 |
| } else if delay == 0 { |
| // TODO: call poll with timeout == 0 |
| return gList{} |
| } else if delay < 1e6 { |
| timeout = 1 |
| } else if delay < 1e15 { |
| timeout = int32(delay / 1e6) |
| } else { |
| // An arbitrary cap on how long to wait for a timer. |
| // 1e9 ms == ~11.5 days. |
| timeout = 1e9 |
| } |
| retry: |
| lock(&mtxpoll) |
| lock(&mtxset) |
| pendingUpdates = 0 |
| unlock(&mtxpoll) |
| |
| n := libc_poll(&pfds[0], int32(len(pfds)), timeout) |
| if n < 0 { |
| e := errno() |
| if e != _EINTR { |
| println("errno=", e, " len(pfds)=", len(pfds)) |
| throw("poll failed") |
| } |
| unlock(&mtxset) |
| // If a timed sleep was interrupted, just return to |
| // recalculate how long we should sleep now. |
| if timeout > 0 { |
| return gList{} |
| } |
| goto retry |
| } |
| // Check if some descriptors need to be changed |
| if n != 0 && pfds[0].revents&(_POLLIN|_POLLHUP|_POLLERR) != 0 { |
| if delay != 0 { |
| // A netpollwakeup could be picked up by a |
| // non-blocking poll. Only clear the wakeup |
| // if blocking. |
| var b [1]byte |
| for read(rdwake, unsafe.Pointer(&b[0]), 1) == 1 { |
| } |
| } |
| // Still look at the other fds even if the mode may have |
| // changed, as netpollBreak might have been called. |
| n-- |
| } |
| var toRun gList |
| for i := 1; 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 { |
| pds[i].everr = false |
| if pfd.revents == _POLLERR { |
| pds[i].everr = true |
| } |
| netpollready(&toRun, pds[i], mode) |
| n-- |
| } |
| } |
| unlock(&mtxset) |
| return toRun |
| } |