mirror of https://go.googlesource.com/go
421 lines
8.9 KiB
Go
421 lines
8.9 KiB
Go
// 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.
|
|
|
|
//go:build aix
|
|
|
|
package runtime
|
|
|
|
import (
|
|
"internal/abi"
|
|
"internal/runtime/atomic"
|
|
"unsafe"
|
|
)
|
|
|
|
const (
|
|
threadStackSize = 0x100000 // size of a thread stack allocated by OS
|
|
)
|
|
|
|
// funcDescriptor is a structure representing a function descriptor
|
|
// A variable with this type is always created in assembler
|
|
type funcDescriptor struct {
|
|
fn uintptr
|
|
toc uintptr
|
|
envPointer uintptr // unused in Golang
|
|
}
|
|
|
|
type mOS struct {
|
|
waitsema uintptr // semaphore for parking on locks
|
|
perrno uintptr // pointer to tls errno
|
|
}
|
|
|
|
//go:nosplit
|
|
func semacreate(mp *m) {
|
|
if mp.waitsema != 0 {
|
|
return
|
|
}
|
|
|
|
var sem *semt
|
|
|
|
// Call libc's malloc rather than malloc. This will
|
|
// allocate space on the C heap. We can't call mallocgc
|
|
// here because it could cause a deadlock.
|
|
sem = (*semt)(malloc(unsafe.Sizeof(*sem)))
|
|
if sem_init(sem, 0, 0) != 0 {
|
|
throw("sem_init")
|
|
}
|
|
mp.waitsema = uintptr(unsafe.Pointer(sem))
|
|
}
|
|
|
|
//go:nosplit
|
|
func semasleep(ns int64) int32 {
|
|
mp := getg().m
|
|
if ns >= 0 {
|
|
var ts timespec
|
|
|
|
if clock_gettime(_CLOCK_REALTIME, &ts) != 0 {
|
|
throw("clock_gettime")
|
|
}
|
|
ts.tv_sec += ns / 1e9
|
|
ts.tv_nsec += ns % 1e9
|
|
if ts.tv_nsec >= 1e9 {
|
|
ts.tv_sec++
|
|
ts.tv_nsec -= 1e9
|
|
}
|
|
|
|
if r, err := sem_timedwait((*semt)(unsafe.Pointer(mp.waitsema)), &ts); r != 0 {
|
|
if err == _ETIMEDOUT || err == _EAGAIN || err == _EINTR {
|
|
return -1
|
|
}
|
|
println("sem_timedwait err ", err, " ts.tv_sec ", ts.tv_sec, " ts.tv_nsec ", ts.tv_nsec, " ns ", ns, " id ", mp.id)
|
|
throw("sem_timedwait")
|
|
}
|
|
return 0
|
|
}
|
|
for {
|
|
r1, err := sem_wait((*semt)(unsafe.Pointer(mp.waitsema)))
|
|
if r1 == 0 {
|
|
break
|
|
}
|
|
if err == _EINTR {
|
|
continue
|
|
}
|
|
throw("sem_wait")
|
|
}
|
|
return 0
|
|
}
|
|
|
|
//go:nosplit
|
|
func semawakeup(mp *m) {
|
|
if sem_post((*semt)(unsafe.Pointer(mp.waitsema))) != 0 {
|
|
throw("sem_post")
|
|
}
|
|
}
|
|
|
|
func osinit() {
|
|
// Call miniterrno so that we can safely make system calls
|
|
// before calling minit on m0.
|
|
miniterrno()
|
|
|
|
ncpu = int32(sysconf(__SC_NPROCESSORS_ONLN))
|
|
physPageSize = sysconf(__SC_PAGE_SIZE)
|
|
}
|
|
|
|
// newosproc0 is a version of newosproc that can be called before the runtime
|
|
// is initialized.
|
|
//
|
|
// This function is not safe to use after initialization as it does not pass an M as fnarg.
|
|
//
|
|
//go:nosplit
|
|
func newosproc0(stacksize uintptr, fn *funcDescriptor) {
|
|
var (
|
|
attr pthread_attr
|
|
oset sigset
|
|
tid pthread
|
|
)
|
|
|
|
if pthread_attr_init(&attr) != 0 {
|
|
writeErrStr(failthreadcreate)
|
|
exit(1)
|
|
}
|
|
|
|
if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
|
|
writeErrStr(failthreadcreate)
|
|
exit(1)
|
|
}
|
|
|
|
if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
|
|
writeErrStr(failthreadcreate)
|
|
exit(1)
|
|
}
|
|
|
|
// Disable signals during create, so that the new thread starts
|
|
// with signals disabled. It will enable them in minit.
|
|
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
|
|
var ret int32
|
|
for tries := 0; tries < 20; tries++ {
|
|
// pthread_create can fail with EAGAIN for no reasons
|
|
// but it will be ok if it retries.
|
|
ret = pthread_create(&tid, &attr, fn, nil)
|
|
if ret != _EAGAIN {
|
|
break
|
|
}
|
|
usleep(uint32(tries+1) * 1000) // Milliseconds.
|
|
}
|
|
sigprocmask(_SIG_SETMASK, &oset, nil)
|
|
if ret != 0 {
|
|
writeErrStr(failthreadcreate)
|
|
exit(1)
|
|
}
|
|
|
|
}
|
|
|
|
// Called to do synchronous initialization of Go code built with
|
|
// -buildmode=c-archive or -buildmode=c-shared.
|
|
// None of the Go runtime is initialized.
|
|
//
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func libpreinit() {
|
|
initsig(true)
|
|
}
|
|
|
|
// Ms related functions
|
|
func mpreinit(mp *m) {
|
|
mp.gsignal = malg(32 * 1024) // AIX wants >= 8K
|
|
mp.gsignal.m = mp
|
|
}
|
|
|
|
// errno address must be retrieved by calling _Errno libc function.
|
|
// This will return a pointer to errno.
|
|
func miniterrno() {
|
|
mp := getg().m
|
|
r, _ := syscall0(&libc__Errno)
|
|
mp.perrno = r
|
|
|
|
}
|
|
|
|
func minit() {
|
|
miniterrno()
|
|
minitSignals()
|
|
getg().m.procid = uint64(pthread_self())
|
|
}
|
|
|
|
func unminit() {
|
|
unminitSignals()
|
|
getg().m.procid = 0
|
|
}
|
|
|
|
// Called from exitm, but not from drop, to undo the effect of thread-owned
|
|
// resources in minit, semacreate, or elsewhere. Do not take locks after calling this.
|
|
func mdestroy(mp *m) {
|
|
}
|
|
|
|
// tstart is a function descriptor to _tstart defined in assembly.
|
|
var tstart funcDescriptor
|
|
|
|
func newosproc(mp *m) {
|
|
var (
|
|
attr pthread_attr
|
|
oset sigset
|
|
tid pthread
|
|
)
|
|
|
|
if pthread_attr_init(&attr) != 0 {
|
|
throw("pthread_attr_init")
|
|
}
|
|
|
|
if pthread_attr_setstacksize(&attr, threadStackSize) != 0 {
|
|
throw("pthread_attr_getstacksize")
|
|
}
|
|
|
|
if pthread_attr_setdetachstate(&attr, _PTHREAD_CREATE_DETACHED) != 0 {
|
|
throw("pthread_attr_setdetachstate")
|
|
}
|
|
|
|
// Disable signals during create, so that the new thread starts
|
|
// with signals disabled. It will enable them in minit.
|
|
sigprocmask(_SIG_SETMASK, &sigset_all, &oset)
|
|
ret := retryOnEAGAIN(func() int32 {
|
|
return pthread_create(&tid, &attr, &tstart, unsafe.Pointer(mp))
|
|
})
|
|
sigprocmask(_SIG_SETMASK, &oset, nil)
|
|
if ret != 0 {
|
|
print("runtime: failed to create new OS thread (have ", mcount(), " already; errno=", ret, ")\n")
|
|
if ret == _EAGAIN {
|
|
println("runtime: may need to increase max user processes (ulimit -u)")
|
|
}
|
|
throw("newosproc")
|
|
}
|
|
|
|
}
|
|
|
|
func exitThread(wait *atomic.Uint32) {
|
|
// We should never reach exitThread on AIX because we let
|
|
// libc clean up threads.
|
|
throw("exitThread")
|
|
}
|
|
|
|
var urandom_dev = []byte("/dev/urandom\x00")
|
|
|
|
//go:nosplit
|
|
func readRandom(r []byte) int {
|
|
fd := open(&urandom_dev[0], 0 /* O_RDONLY */, 0)
|
|
n := read(fd, unsafe.Pointer(&r[0]), int32(len(r)))
|
|
closefd(fd)
|
|
return int(n)
|
|
}
|
|
|
|
func goenvs() {
|
|
goenvs_unix()
|
|
}
|
|
|
|
/* SIGNAL */
|
|
|
|
const (
|
|
_NSIG = 256
|
|
)
|
|
|
|
// sigtramp is a function descriptor to _sigtramp defined in assembly
|
|
var sigtramp funcDescriptor
|
|
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func setsig(i uint32, fn uintptr) {
|
|
var sa sigactiont
|
|
sa.sa_flags = _SA_SIGINFO | _SA_ONSTACK | _SA_RESTART
|
|
sa.sa_mask = sigset_all
|
|
if fn == abi.FuncPCABIInternal(sighandler) { // abi.FuncPCABIInternal(sighandler) matches the callers in signal_unix.go
|
|
fn = uintptr(unsafe.Pointer(&sigtramp))
|
|
}
|
|
sa.sa_handler = fn
|
|
sigaction(uintptr(i), &sa, nil)
|
|
|
|
}
|
|
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func setsigstack(i uint32) {
|
|
var sa sigactiont
|
|
sigaction(uintptr(i), nil, &sa)
|
|
if sa.sa_flags&_SA_ONSTACK != 0 {
|
|
return
|
|
}
|
|
sa.sa_flags |= _SA_ONSTACK
|
|
sigaction(uintptr(i), &sa, nil)
|
|
}
|
|
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func getsig(i uint32) uintptr {
|
|
var sa sigactiont
|
|
sigaction(uintptr(i), nil, &sa)
|
|
return sa.sa_handler
|
|
}
|
|
|
|
// setSignalstackSP sets the ss_sp field of a stackt.
|
|
//
|
|
//go:nosplit
|
|
func setSignalstackSP(s *stackt, sp uintptr) {
|
|
*(*uintptr)(unsafe.Pointer(&s.ss_sp)) = sp
|
|
}
|
|
|
|
//go:nosplit
|
|
func (c *sigctxt) fixsigcode(sig uint32) {
|
|
switch sig {
|
|
case _SIGPIPE:
|
|
// For SIGPIPE, c.sigcode() isn't set to _SI_USER as on Linux.
|
|
// Therefore, raisebadsignal won't raise SIGPIPE again if
|
|
// it was deliver in a non-Go thread.
|
|
c.set_sigcode(_SI_USER)
|
|
}
|
|
}
|
|
|
|
//go:nosplit
|
|
//go:nowritebarrierrec
|
|
func sigaddset(mask *sigset, i int) {
|
|
(*mask)[(i-1)/64] |= 1 << ((uint32(i) - 1) & 63)
|
|
}
|
|
|
|
func sigdelset(mask *sigset, i int) {
|
|
(*mask)[(i-1)/64] &^= 1 << ((uint32(i) - 1) & 63)
|
|
}
|
|
|
|
func setProcessCPUProfiler(hz int32) {
|
|
setProcessCPUProfilerTimer(hz)
|
|
}
|
|
|
|
func setThreadCPUProfiler(hz int32) {
|
|
setThreadCPUProfilerHz(hz)
|
|
}
|
|
|
|
//go:nosplit
|
|
func validSIGPROF(mp *m, c *sigctxt) bool {
|
|
return true
|
|
}
|
|
|
|
const (
|
|
_CLOCK_REALTIME = 9
|
|
_CLOCK_MONOTONIC = 10
|
|
)
|
|
|
|
//go:nosplit
|
|
func nanotime1() int64 {
|
|
tp := ×pec{}
|
|
if clock_gettime(_CLOCK_REALTIME, tp) != 0 {
|
|
throw("syscall clock_gettime failed")
|
|
}
|
|
return tp.tv_sec*1000000000 + tp.tv_nsec
|
|
}
|
|
|
|
func walltime() (sec int64, nsec int32) {
|
|
ts := ×pec{}
|
|
if clock_gettime(_CLOCK_REALTIME, ts) != 0 {
|
|
throw("syscall clock_gettime failed")
|
|
}
|
|
return ts.tv_sec, int32(ts.tv_nsec)
|
|
}
|
|
|
|
//go:nosplit
|
|
func fcntl(fd, cmd, arg int32) (int32, int32) {
|
|
r, errno := syscall3(&libc_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg))
|
|
return int32(r), int32(errno)
|
|
}
|
|
|
|
//go:nosplit
|
|
func setNonblock(fd int32) {
|
|
flags, _ := fcntl(fd, _F_GETFL, 0)
|
|
if flags != -1 {
|
|
fcntl(fd, _F_SETFL, flags|_O_NONBLOCK)
|
|
}
|
|
}
|
|
|
|
// sigPerThreadSyscall is only used on linux, so we assign a bogus signal
|
|
// number.
|
|
const sigPerThreadSyscall = 1 << 31
|
|
|
|
//go:nosplit
|
|
func runPerThreadSyscall() {
|
|
throw("runPerThreadSyscall only valid on linux")
|
|
}
|
|
|
|
//go:nosplit
|
|
func getuid() int32 {
|
|
r, errno := syscall0(&libc_getuid)
|
|
if errno != 0 {
|
|
print("getuid failed ", errno)
|
|
throw("getuid")
|
|
}
|
|
return int32(r)
|
|
}
|
|
|
|
//go:nosplit
|
|
func geteuid() int32 {
|
|
r, errno := syscall0(&libc_geteuid)
|
|
if errno != 0 {
|
|
print("geteuid failed ", errno)
|
|
throw("geteuid")
|
|
}
|
|
return int32(r)
|
|
}
|
|
|
|
//go:nosplit
|
|
func getgid() int32 {
|
|
r, errno := syscall0(&libc_getgid)
|
|
if errno != 0 {
|
|
print("getgid failed ", errno)
|
|
throw("getgid")
|
|
}
|
|
return int32(r)
|
|
}
|
|
|
|
//go:nosplit
|
|
func getegid() int32 {
|
|
r, errno := syscall0(&libc_getegid)
|
|
if errno != 0 {
|
|
print("getegid failed ", errno)
|
|
throw("getegid")
|
|
}
|
|
return int32(r)
|
|
}
|