summaryrefslogtreecommitdiffstats
path: root/src/runtime/os_aix.go
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/runtime/os_aix.go420
1 files changed, 420 insertions, 0 deletions
diff --git a/src/runtime/os_aix.go b/src/runtime/os_aix.go
new file mode 100644
index 0000000..3a5078a
--- /dev/null
+++ b/src/runtime/os_aix.go
@@ -0,0 +1,420 @@
+// 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"
+ "runtime/internal/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 := &timespec{}
+ 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 := &timespec{}
+ 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)
+}