aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/os_darwin.go
diff options
context:
space:
mode:
authorKeith Randall <khr@google.com>2018-06-13 14:39:36 -0700
committerKeith Randall <khr@golang.org>2018-06-16 00:58:37 +0000
commit5b5473c6bf0b91fef76e21493c70754144ff0214 (patch)
treee7920ae9c7030d59846d259caa36dd15111cb906 /src/runtime/os_darwin.go
parentc34381a6581c59b8e80d2f2ef3ad9ec223b752a5 (diff)
downloadgo-5b5473c6bf0b91fef76e21493c70754144ff0214.tar.gz
go-5b5473c6bf0b91fef76e21493c70754144ff0214.zip
runtime: move semaphore ops from system calls to libc calls on Darwin
This CL removes the last of the direct system calls in the runtime package. This is the last CL for 1.11. Use libcCall instead of asmcgocall in a few places I accidentally used the wrong one. For 1.12, we need to think about whether/how the syscall package should be moved over to libc. Update #17490 Change-Id: I4f0bd9cd6023f662f2e29588266fdfae5233898f Reviewed-on: https://go-review.googlesource.com/118736 Run-TryBot: Keith Randall <khr@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/runtime/os_darwin.go')
-rw-r--r--src/runtime/os_darwin.go324
1 files changed, 45 insertions, 279 deletions
diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go
index 8024d443a2..5019b9423c 100644
--- a/src/runtime/os_darwin.go
+++ b/src/runtime/os_darwin.go
@@ -7,35 +7,63 @@ package runtime
import "unsafe"
type mOS struct {
- machport uint32 // return address for mach ipc
- waitsema uint32 // semaphore for parking on locks
+ initialized bool
+ mutex pthreadmutex
+ cond pthreadcond
+ count int
}
-//go:noescape
-func mach_msg_trap(h unsafe.Pointer, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32
-
-func mach_reply_port() uint32
-func mach_task_self() uint32
-func mach_thread_self() uint32
-
func unimplemented(name string) {
println(name, "not implemented")
*(*int)(unsafe.Pointer(uintptr(1231))) = 1231
}
//go:nosplit
-func semawakeup(mp *m) {
- mach_semrelease(mp.waitsema)
+func semacreate(mp *m) {
+ if mp.initialized {
+ return
+ }
+ mp.initialized = true
+ if err := pthread_mutex_init(&mp.mutex, nil); err != 0 {
+ throw("pthread_mutex_init")
+ }
+ if err := pthread_cond_init(&mp.cond, nil); err != 0 {
+ throw("pthread_cond_init")
+ }
}
//go:nosplit
-func semacreate(mp *m) {
- if mp.waitsema != 0 {
- return
+func semasleep(ns int64) int32 {
+ mp := getg().m
+ pthread_mutex_lock(&mp.mutex)
+ for {
+ if mp.count > 0 {
+ mp.count--
+ pthread_mutex_unlock(&mp.mutex)
+ return 0
+ }
+ if ns >= 0 {
+ var t timespec
+ t.set_nsec(ns)
+ err := pthread_cond_timedwait(&mp.cond, &mp.mutex, &t)
+ if err == _ETIMEDOUT {
+ pthread_mutex_unlock(&mp.mutex)
+ return -1
+ }
+ } else {
+ pthread_cond_wait(&mp.cond, &mp.mutex)
+ }
}
- systemstack(func() {
- mp.waitsema = mach_semcreate()
- })
+}
+
+//go:nosplit
+func semawakeup(mp *m) {
+ pthread_mutex_lock(&mp.mutex)
+ mp.count++
+ if mp.count > 0 {
+ pthread_cond_signal(&mp.cond)
+ }
+ pthread_mutex_unlock(&mp.mutex)
}
// BSD interface for threading.
@@ -219,268 +247,6 @@ func unminit() {
}
}
-// Mach IPC, to get at semaphores
-// Definitions are in /usr/include/mach on a Mac.
-
-func macherror(r int32, fn string) {
- print("mach error ", fn, ": ", r, "\n")
- throw("mach error")
-}
-
-const _DebugMach = false
-
-var zerondr machndr
-
-func mach_msgh_bits(a, b uint32) uint32 {
- return a | b<<8
-}
-
-func mach_msg(h *machheader, op int32, send_size, rcv_size, rcv_name, timeout, notify uint32) int32 {
- // TODO: Loop on interrupt.
- return mach_msg_trap(unsafe.Pointer(h), op, send_size, rcv_size, rcv_name, timeout, notify)
-}
-
-// Mach RPC (MIG)
-const (
- _MinMachMsg = 48
- _MachReply = 100
-)
-
-type codemsg struct {
- h machheader
- ndr machndr
- code int32
-}
-
-func machcall(h *machheader, maxsize int32, rxsize int32) int32 {
- _g_ := getg()
- port := _g_.m.machport
- if port == 0 {
- port = mach_reply_port()
- _g_.m.machport = port
- }
-
- h.msgh_bits |= mach_msgh_bits(_MACH_MSG_TYPE_COPY_SEND, _MACH_MSG_TYPE_MAKE_SEND_ONCE)
- h.msgh_local_port = port
- h.msgh_reserved = 0
- id := h.msgh_id
-
- if _DebugMach {
- p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
- print("send:\t")
- var i uint32
- for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
- print(" ", p[i])
- if i%8 == 7 {
- print("\n\t")
- }
- }
- if i%8 != 0 {
- print("\n")
- }
- }
- ret := mach_msg(h, _MACH_SEND_MSG|_MACH_RCV_MSG, h.msgh_size, uint32(maxsize), port, 0, 0)
- if ret != 0 {
- if _DebugMach {
- print("mach_msg error ", ret, "\n")
- }
- return ret
- }
- if _DebugMach {
- p := (*[10000]unsafe.Pointer)(unsafe.Pointer(h))
- var i uint32
- for i = 0; i < h.msgh_size/uint32(unsafe.Sizeof(p[0])); i++ {
- print(" ", p[i])
- if i%8 == 7 {
- print("\n\t")
- }
- }
- if i%8 != 0 {
- print("\n")
- }
- }
- if h.msgh_id != id+_MachReply {
- if _DebugMach {
- print("mach_msg _MachReply id mismatch ", h.msgh_id, " != ", id+_MachReply, "\n")
- }
- return -303 // MIG_REPLY_MISMATCH
- }
- // Look for a response giving the return value.
- // Any call can send this back with an error,
- // and some calls only have return values so they
- // send it back on success too. I don't quite see how
- // you know it's one of these and not the full response
- // format, so just look if the message is right.
- c := (*codemsg)(unsafe.Pointer(h))
- if uintptr(h.msgh_size) == unsafe.Sizeof(*c) && h.msgh_bits&_MACH_MSGH_BITS_COMPLEX == 0 {
- if _DebugMach {
- print("mig result ", c.code, "\n")
- }
- return c.code
- }
- if h.msgh_size != uint32(rxsize) {
- if _DebugMach {
- print("mach_msg _MachReply size mismatch ", h.msgh_size, " != ", rxsize, "\n")
- }
- return -307 // MIG_ARRAY_TOO_LARGE
- }
- return 0
-}
-
-// Semaphores!
-
-const (
- tmach_semcreate = 3418
- rmach_semcreate = tmach_semcreate + _MachReply
-
- tmach_semdestroy = 3419
- rmach_semdestroy = tmach_semdestroy + _MachReply
-
- _KERN_ABORTED = 14
- _KERN_OPERATION_TIMED_OUT = 49
-)
-
-type tmach_semcreatemsg struct {
- h machheader
- ndr machndr
- policy int32
- value int32
-}
-
-type rmach_semcreatemsg struct {
- h machheader
- body machbody
- semaphore machport
-}
-
-type tmach_semdestroymsg struct {
- h machheader
- body machbody
- semaphore machport
-}
-
-func mach_semcreate() uint32 {
- var m [256]uint8
- tx := (*tmach_semcreatemsg)(unsafe.Pointer(&m))
- rx := (*rmach_semcreatemsg)(unsafe.Pointer(&m))
-
- tx.h.msgh_bits = 0
- tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
- tx.h.msgh_remote_port = mach_task_self()
- tx.h.msgh_id = tmach_semcreate
- tx.ndr = zerondr
-
- tx.policy = 0 // 0 = SYNC_POLICY_FIFO
- tx.value = 0
-
- for {
- r := machcall(&tx.h, int32(unsafe.Sizeof(m)), int32(unsafe.Sizeof(*rx)))
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
- macherror(r, "semaphore_create")
- }
- if rx.body.msgh_descriptor_count != 1 {
- unimplemented("mach_semcreate desc count")
- }
- return rx.semaphore.name
-}
-
-func mach_semdestroy(sem uint32) {
- var m [256]uint8
- tx := (*tmach_semdestroymsg)(unsafe.Pointer(&m))
-
- tx.h.msgh_bits = _MACH_MSGH_BITS_COMPLEX
- tx.h.msgh_size = uint32(unsafe.Sizeof(*tx))
- tx.h.msgh_remote_port = mach_task_self()
- tx.h.msgh_id = tmach_semdestroy
- tx.body.msgh_descriptor_count = 1
- tx.semaphore.name = sem
- tx.semaphore.disposition = _MACH_MSG_TYPE_MOVE_SEND
- tx.semaphore._type = 0
-
- for {
- r := machcall(&tx.h, int32(unsafe.Sizeof(m)), 0)
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
- macherror(r, "semaphore_destroy")
- }
-}
-
-// The other calls have simple system call traps in sys_darwin_{amd64,386}.s
-
-func mach_semaphore_wait(sema uint32) int32
-func mach_semaphore_timedwait(sema, sec, nsec uint32) int32
-func mach_semaphore_signal(sema uint32) int32
-func mach_semaphore_signal_all(sema uint32) int32
-
-func semasleep1(ns int64) int32 {
- _g_ := getg()
-
- if ns >= 0 {
- var nsecs int32
- secs := timediv(ns, 1000000000, &nsecs)
- r := mach_semaphore_timedwait(_g_.m.waitsema, uint32(secs), uint32(nsecs))
- if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT {
- return -1
- }
- if r != 0 {
- macherror(r, "semaphore_wait")
- }
- return 0
- }
-
- for {
- r := mach_semaphore_wait(_g_.m.waitsema)
- if r == 0 {
- break
- }
- // Note: We don't know how this call (with no timeout) can get _KERN_OPERATION_TIMED_OUT,
- // but it does reliably, though at a very low rate, on OS X 10.8, 10.9, 10.10, and 10.11.
- // See golang.org/issue/17161.
- if r == _KERN_ABORTED || r == _KERN_OPERATION_TIMED_OUT { // interrupted
- continue
- }
- macherror(r, "semaphore_wait")
- }
- return 0
-}
-
-//go:nosplit
-func semasleep(ns int64) int32 {
- var r int32
- systemstack(func() {
- r = semasleep1(ns)
- })
- return r
-}
-
-//go:nosplit
-func mach_semrelease(sem uint32) {
- for {
- r := mach_semaphore_signal(sem)
- if r == 0 {
- break
- }
- if r == _KERN_ABORTED { // interrupted
- continue
- }
-
- // mach_semrelease must be completely nosplit,
- // because it is called from Go code.
- // If we're going to die, start that process on the system stack
- // to avoid a Go stack split.
- systemstack(func() { macherror(r, "semaphore_signal") })
- }
-}
-
//go:nosplit
func osyield() {
usleep(1)