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	"unsafe"
10)
11
12type mOS struct {
13	waitsemacount uint32
14}
15
16//go:noescape
17//extern thrsleep
18func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32
19
20//go:noescape
21//extern thrwakeup
22func thrwakeup(ident uintptr, n int32) int32
23
24//go:nosplit
25func semacreate(mp *m) {
26}
27
28//go:nosplit
29func semasleep(ns int64) int32 {
30	_g_ := getg()
31
32	// Compute sleep deadline.
33	var tsp *timespec
34	if ns >= 0 {
35		var ts timespec
36		var nsec int32
37		ns += nanotime()
38		ts.set_sec(int64(timediv(ns, 1000000000, &nsec)))
39		ts.set_nsec(nsec)
40		tsp = &ts
41	}
42
43	for {
44		v := atomic.Load(&_g_.m.mos.waitsemacount)
45		if v > 0 {
46			if atomic.Cas(&_g_.m.mos.waitsemacount, v, v-1) {
47				return 0 // semaphore acquired
48			}
49			continue
50		}
51
52		// Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0.
53		//
54		// From OpenBSD's __thrsleep(2) manual:
55		// "The abort argument, if not NULL, points to an int that will
56		// be examined [...] immediately before blocking. If that int
57		// is non-zero then __thrsleep() will immediately return EINTR
58		// without blocking."
59		ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.mos.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.mos.waitsemacount)
60		if ret == _EWOULDBLOCK {
61			return -1
62		}
63	}
64}
65
66//go:nosplit
67func semawakeup(mp *m) {
68	atomic.Xadd(&mp.mos.waitsemacount, 1)
69	ret := thrwakeup(uintptr(unsafe.Pointer(&mp.mos.waitsemacount)), 1)
70	if ret != 0 && ret != _ESRCH {
71		// semawakeup can be called on signal stack.
72		systemstack(func() {
73			print("thrwakeup addr=", &mp.mos.waitsemacount, " sem=", mp.mos.waitsemacount, " ret=", ret, "\n")
74		})
75	}
76}
77