1// Copyright 2011 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
17//go:noescape
18//extern thrsleep
19func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32
20
21//go:noescape
22//extern thrwakeup
23func thrwakeup(ident uintptr, n int32) int32
24
25//go:noescape
26//extern sysctl
27func sysctl(*uint32, uint32, *byte, *uintptr, *byte, uintptr) int32
28
29// From OpenBSD's <sys/sysctl.h>
30const (
31	_CTL_KERN   = 1
32	_KERN_OSREV = 3
33
34	_CTL_HW        = 6
35	_HW_NCPU       = 3
36	_HW_PAGESIZE   = 7
37	_HW_NCPUONLINE = 25
38)
39
40func sysctlInt(mib []uint32) (int32, bool) {
41	var out int32
42	nout := unsafe.Sizeof(out)
43	ret := sysctl(&mib[0], uint32(len(mib)), (*byte)(unsafe.Pointer(&out)), &nout, nil, 0)
44	if ret < 0 {
45		return 0, false
46	}
47	return out, true
48}
49
50func getncpu() int32 {
51	// Try hw.ncpuonline first because hw.ncpu would report a number twice as
52	// high as the actual CPUs running on OpenBSD 6.4 with hyperthreading
53	// disabled (hw.smt=0). See https://golang.org/issue/30127
54	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPUONLINE}); ok {
55		return int32(n)
56	}
57	if n, ok := sysctlInt([]uint32{_CTL_HW, _HW_NCPU}); ok {
58		return int32(n)
59	}
60	return 1
61}
62
63func getPageSize() uintptr {
64	if ps, ok := sysctlInt([]uint32{_CTL_HW, _HW_PAGESIZE}); ok {
65		return uintptr(ps)
66	}
67	return 0
68}
69
70func getOSRev() int {
71	if osrev, ok := sysctlInt([]uint32{_CTL_KERN, _KERN_OSREV}); ok {
72		return int(osrev)
73	}
74	return 0
75}
76
77//go:nosplit
78func semacreate(mp *m) {
79}
80
81//go:nosplit
82func semasleep(ns int64) int32 {
83	_g_ := getg()
84
85	// Compute sleep deadline.
86	var tsp *timespec
87	if ns >= 0 {
88		var ts timespec
89		ts.setNsec(ns + nanotime())
90		tsp = &ts
91	}
92
93	for {
94		v := atomic.Load(&_g_.m.mos.waitsemacount)
95		if v > 0 {
96			if atomic.Cas(&_g_.m.mos.waitsemacount, v, v-1) {
97				return 0 // semaphore acquired
98			}
99			continue
100		}
101
102		// Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0.
103		//
104		// From OpenBSD's __thrsleep(2) manual:
105		// "The abort argument, if not NULL, points to an int that will
106		// be examined [...] immediately before blocking. If that int
107		// is non-zero then __thrsleep() will immediately return EINTR
108		// without blocking."
109		ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.mos.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.mos.waitsemacount)
110		if ret == _EWOULDBLOCK {
111			return -1
112		}
113	}
114}
115
116//go:nosplit
117func semawakeup(mp *m) {
118	atomic.Xadd(&mp.mos.waitsemacount, 1)
119	ret := thrwakeup(uintptr(unsafe.Pointer(&mp.mos.waitsemacount)), 1)
120	if ret != 0 && ret != _ESRCH {
121		// semawakeup can be called on signal stack.
122		systemstack(func() {
123			print("thrwakeup addr=", &mp.mos.waitsemacount, " sem=", mp.mos.waitsemacount, " ret=", ret, "\n")
124		})
125	}
126}
127
128func osinit() {
129	ncpu = getncpu()
130	physPageSize = getPageSize()
131	haveMapStack = getOSRev() >= 201805 // OpenBSD 6.3
132}
133
134var haveMapStack = false
135