1// Copyright 2014 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package runtime
6
7import (
8	"runtime/internal/atomic"
9	"runtime/internal/sys"
10	"unsafe"
11)
12
13type mOS struct {
14	waitsemacount uint32
15}
16
17func getProcID() uint64 {
18	return uint64(lwp_self())
19}
20
21//extern-sysinfo _lwp_self
22func lwp_self() int32
23
24//go:noescape
25//extern-sysinfo _lwp_park
26func lwp_park(ts int32, rel int32, abstime *timespec, unpark int32, hint, unparkhint unsafe.Pointer) int32
27
28//go:noescape
29//extern-sysinfo _lwp_unpark
30func lwp_unpark(lwp int32, hint unsafe.Pointer) int32
31
32//go:noescape
33//extern-sysinfo sysctl
34func sysctl(*uint32, uint32, *byte, *uintptr, *byte, uintptr) int32
35
36func sysctlInt(mib []uint32) (int32, bool) {
37	var out int32
38	nout := unsafe.Sizeof(out)
39	ret := sysctl(&mib[0], uint32(len(mib)), (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
40	if ret < 0 {
41		return 0, false
42	}
43	return out, true
44}
45
46func getncpu() int32 {
47	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPUONLINE}); ok {
48		return int32(n)
49	}
50	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPU}); ok {
51		return int32(n)
52	}
53	return 1
54}
55
56func getPageSize() uintptr {
57	mib := [2]uint32{_CTL_HW, _HW_PAGESIZE}
58	out := uint32(0)
59	nout := unsafe.Sizeof(out)
60	ret := sysctl(&mib[0], 2, (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
61	if ret >= 0 {
62		return uintptr(out)
63	}
64	return 0
65}
66
67//go:nosplit
68func semacreate(mp *m) {
69}
70
71//go:nosplit
72func semasleep(ns int64) int32 {
73	_g_ := getg()
74	var deadline int64
75	if ns >= 0 {
76		deadline = nanotime() + ns
77	}
78
79	for {
80		v := atomic.Load(&_g_.m.waitsemacount)
81		if v > 0 {
82			if atomic.Cas(&_g_.m.waitsemacount, v, v-1) {
83				return 0 // semaphore acquired
84			}
85			continue
86		}
87
88		// Sleep until unparked by semawakeup or timeout.
89		var tsp *timespec
90		var ts timespec
91		if ns >= 0 {
92			wait := deadline - nanotime()
93			if wait <= 0 {
94				return -1
95			}
96			ts.setNsec(wait)
97			tsp = &ts
98		}
99		ret := lwp_park(_CLOCK_MONOTONIC, _TIMER_RELTIME, tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
100		if ret != 0 && errno() == _ETIMEDOUT {
101			return -1
102		}
103	}
104}
105
106//go:nosplit
107func semawakeup(mp *m) {
108	atomic.Xadd(&mp.waitsemacount, 1)
109	// From NetBSD's _lwp_unpark(2) manual:
110	// "If the target LWP is not currently waiting, it will return
111	// immediately upon the next call to _lwp_park()."
112	ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
113	if ret != 0 && errno() != _ESRCH {
114		// semawakeup can be called on signal stack.
115		systemstack(func() {
116			print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " errno=", errno(), "\n")
117		})
118	}
119}
120
121func osinit() {
122	ncpu = getncpu()
123	if physPageSize == 0 {
124		physPageSize = getPageSize()
125	}
126}
127
128func sysargs(argc int32, argv **byte) {
129	n := argc + 1
130
131	// skip over argv, envp to get to auxv
132	for argv_index(argv, n) != nil {
133		n++
134	}
135
136	// skip NULL separator
137	n++
138
139	// now argv+n is auxv
140	auxv := (*[1 << 28]uintptr)(add(unsafe.Pointer(argv), uintptr(n)*sys.PtrSize))
141	sysauxv(auxv[:])
142}
143
144const (
145	_AT_NULL   = 0 // Terminates the vector
146	_AT_PAGESZ = 6 // Page size in bytes
147)
148
149func sysauxv(auxv []uintptr) {
150	for i := 0; auxv[i] != _AT_NULL; i += 2 {
151		tag, val := auxv[i], auxv[i+1]
152		switch tag {
153		case _AT_PAGESZ:
154			physPageSize = val
155		}
156	}
157}
158