Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
go/src/runtime/netpoll_aix.go
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
226 lines (195 sloc)
4.92 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2018 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 ( | |
"runtime/internal/atomic" | |
"unsafe" | |
) | |
// 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 | |
//go:cgo_import_dynamic libc_poll poll "libc.a/shr_64.o" | |
//go:linkname libc_poll libc_poll | |
var libc_poll libFunc | |
//go:nosplit | |
func poll(pfds *pollfd, npfds uintptr, timeout uintptr) (int32, int32) { | |
r, err := syscall3(&libc_poll, uintptr(unsafe.Pointer(pfds)), npfds, timeout) | |
return int32(r), int32(err) | |
} | |
// pollfd represents the poll structure for AIX operating system. | |
type pollfd struct { | |
fd int32 | |
events int16 | |
revents int16 | |
} | |
const _POLLIN = 0x0001 | |
const _POLLOUT = 0x0002 | |
const _POLLHUP = 0x2000 | |
const _POLLERR = 0x4000 | |
var ( | |
pfds []pollfd | |
pds []*pollDesc | |
mtxpoll mutex | |
mtxset mutex | |
rdwake int32 | |
wrwake int32 | |
pendingUpdates int32 | |
netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak | |
) | |
func netpollinit() { | |
// Create the pipe we use to wakeup poll. | |
r, w, errno := nonblockingPipe() | |
if errno != 0 { | |
throw("netpollinit: failed to create pipe") | |
} | |
rdwake = r | |
wrwake = w | |
// Pre-allocate array of pollfd structures for poll. | |
pfds = make([]pollfd, 1, 128) | |
// Poll the read side of the pipe. | |
pfds[0].fd = rdwake | |
pfds[0].events = _POLLIN | |
pds = make([]*pollDesc, 1, 128) | |
pds[0] = nil | |
} | |
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 | |
b := [1]byte{0} | |
write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) | |
} | |
} | |
func netpollopen(fd uintptr, pd *pollDesc) int32 { | |
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 { | |
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) { | |
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 a poll. | |
func netpollBreak() { | |
// Failing to cas indicates there is an in-flight wakeup, so we're done here. | |
if !netpollWakeSig.CompareAndSwap(0, 1) { | |
return | |
} | |
b := [1]byte{0} | |
write(uintptr(wrwake), unsafe.Pointer(&b[0]), 1) | |
} | |
// 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 { | |
var timeout uintptr | |
if delay < 0 { | |
timeout = ^uintptr(0) | |
} else if delay == 0 { | |
// TODO: call poll with timeout == 0 | |
return gList{} | |
} else if delay < 1e6 { | |
timeout = 1 | |
} else if delay < 1e15 { | |
timeout = uintptr(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, e := poll(&pfds[0], uintptr(len(pfds)), timeout) | |
if n < 0 { | |
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 { | |
} | |
netpollWakeSig.Store(0) | |
} | |
// 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].setEventErr(pfd.revents == _POLLERR) | |
netpollready(&toRun, pds[i], mode) | |
n-- | |
} | |
} | |
unlock(&mtxset) | |
return toRun | |
} |