1
0
mirror of https://github.com/golang/go synced 2024-11-18 04:04:49 -07:00

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 <dvyukov@google.com>
Reviewed-by: Joel Sing <jsing@google.com>
This commit is contained in:
Matthew Dempsky 2015-02-23 17:05:30 +09:00 committed by Joel Sing
parent 1fda57ba72
commit 9f926e81c2
2 changed files with 15 additions and 27 deletions

View File

@ -62,25 +62,22 @@ func semasleep(ns int64) int32 {
} }
for { for {
// spin-mutex lock v := atomicload(&_g_.m.waitsemacount)
for { if v > 0 {
if xchg(&_g_.m.waitsemalock, 1) == 0 { if cas(&_g_.m.waitsemacount, v, v-1) {
break return 0 // semaphore acquired
} }
osyield() continue
} }
if _g_.m.waitsemacount != 0 { // Sleep until woken by semawakeup or timeout; or abort if waitsemacount != 0.
// semaphore is available. //
_g_.m.waitsemacount-- // From OpenBSD's __thrsleep(2) manual:
// spin-mutex unlock // "The abort argument, if not NULL, points to an int that will
atomicstore(&_g_.m.waitsemalock, 0) // be examined [...] immediately before blocking. If that int
return 0 // semaphore acquired // 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)
// 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)))
if ret == _EWOULDBLOCK { if ret == _EWOULDBLOCK {
return -1 return -1
} }
@ -89,14 +86,7 @@ func semasleep(ns int64) int32 {
//go:nosplit //go:nosplit
func semawakeup(mp *m) { func semawakeup(mp *m) {
// spin-mutex lock xadd(&mp.waitsemacount, 1)
for {
if xchg(&mp.waitsemalock, 1) == 0 {
break
}
osyield()
}
mp.waitsemacount++
ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1) ret := thrwakeup(uintptr(unsafe.Pointer(&mp.waitsemacount)), 1)
if ret != 0 && ret != _ESRCH { if ret != 0 && ret != _ESRCH {
// semawakeup can be called on signal stack. // 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") 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) { func newosproc(mp *m, stk unsafe.Pointer) {

View File

@ -26,7 +26,7 @@ func raiseproc(sig int32)
func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32 func tfork(param *tforkt, psize uintptr, mm *m, gg *g, fn uintptr) int32
//go:noescape //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 //go:noescape
func thrwakeup(ident uintptr, n int32) int32 func thrwakeup(ident uintptr, n int32) int32