From 9f926e81c262f11e2980a25f06ac17f3bbeb378a Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 23 Feb 2015 17:05:30 +0900 Subject: [PATCH] runtime: simplify OpenBSD semaphores OpenBSD's thrsleep system call includes an "abort" parameter, which specifies a memory address to be tested after being registered on the sleep channel (i.e., capable of being woken up by thrwakeup). By passing a pointer to waitsemacount for this parameter, we avoid race conditions without needing a lock. Instead we just need to use atomicload, cas, and xadd to mutate the semaphore count. Change-Id: If9f2ab7cfd682da217f9912783cadea7e72283a8 Reviewed-on: https://go-review.googlesource.com/5563 Reviewed-by: Dmitry Vyukov Reviewed-by: Joel Sing --- src/runtime/os1_openbsd.go | 40 +++++++++++++------------------------- src/runtime/os_openbsd.go | 2 +- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go index dcf205b841..d23d812ace 100644 --- a/src/runtime/os1_openbsd.go +++ b/src/runtime/os1_openbsd.go @@ -62,25 +62,22 @@ func semasleep(ns int64) int32 { } for { - // spin-mutex lock - for { - if xchg(&_g_.m.waitsemalock, 1) == 0 { - break + v := atomicload(&_g_.m.waitsemacount) + if v > 0 { + if cas(&_g_.m.waitsemacount, v, v-1) { + return 0 // semaphore acquired } - osyield() + continue } - if _g_.m.waitsemacount != 0 { - // semaphore is available. - _g_.m.waitsemacount-- - // spin-mutex unlock - atomicstore(&_g_.m.waitsemalock, 0) - return 0 // semaphore acquired - } - - // sleep until semaphore != 0 or timeout. - // thrsleep unlocks m.waitsemalock. - ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, uintptr(unsafe.Pointer(&_g_.m.waitsemalock)), (*int32)(unsafe.Pointer(&_g_.m.waitsemacount))) + // Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0. + // + // From OpenBSD's __thrsleep(2) manual: + // "The abort argument, if not NULL, points to an int that will + // be examined [...] immediately before blocking. If that int + // is non-zero then __thrsleep() will immediately return EINTR + // without blocking." + ret := thrsleep(uintptr(unsafe.Pointer(&_g_.m.waitsemacount)), _CLOCK_MONOTONIC, tsp, 0, &_g_.m.waitsemacount) if ret == _EWOULDBLOCK { return -1 } @@ -89,14 +86,7 @@ func semasleep(ns int64) int32 { //go:nosplit func semawakeup(mp *m) { - // spin-mutex lock - for { - if xchg(&mp.waitsemalock, 1) == 0 { - break - } - osyield() - } - mp.waitsemacount++ + xadd(&mp.waitsemacount, 1) ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1) if ret != 0 && ret != _ESRCH { // semawakeup can be called on signal stack. @@ -104,8 +94,6 @@ func semawakeup(mp *m) { print("thrwakeup addr=", &mp.waitsemacount, " sem=", mp.waitsemacount, " ret=", ret, "\n") }) } - // spin-mutex unlock - atomicstore(&mp.waitsemalock, 0) } func newosproc(mp *m, stk unsafe.Pointer) { diff --git a/src/runtime/os_openbsd.go b/src/runtime/os_openbsd.go index c1a55d6477..8a97a738f7 100644 --- a/src/runtime/os_openbsd.go +++ b/src/runtime/os_openbsd.go @@ -26,7 +26,7 @@ func raiseproc(sig int32) func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32 //go:noescape -func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *int32) int32 +func thrsleep(ident uintptr, clock_id int32, tsp *timespec, lock uintptr, abort *uint32) int32 //go:noescape func thrwakeup(ident uintptr, n int32) int32