mirror of
https://github.com/golang/go
synced 2024-11-19 05:44:40 -07:00
runtime: simplify NetBSD semaphores
NetBSD's semaphore implementation is derived from OpenBSD's, but has subsequently diverged due to cleanups that were only applied to the latter (https://golang.org/cl/137960043, https://golang.org/cl/5563). This CL applies analogous cleanups for NetBSD. Notably, we can also remove the scary NetBSD deadlock warning. NetBSD's manual pages document that lwp_unpark on a not-yet-parked LWP will cause that LWP's next lwp_park system call to return immediately, so there's no race hazard. Change-Id: Ib06844c420d2496ac289748eba13eb4700bbbbb2 Reviewed-on: https://go-review.googlesource.com/5564 Reviewed-by: Dmitry Vyukov <dvyukov@google.com> Reviewed-by: Joel Sing <jsing@google.com>
This commit is contained in:
parent
c123a80063
commit
2fdb728d01
@ -7,8 +7,8 @@ package runtime
|
|||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
const (
|
const (
|
||||||
_ESRCH = 3
|
_ESRCH = 3
|
||||||
_ENOTSUP = 91
|
_ETIMEDOUT = 60
|
||||||
|
|
||||||
// From NetBSD's <sys/time.h>
|
// From NetBSD's <sys/time.h>
|
||||||
_CLOCK_REALTIME = 0
|
_CLOCK_REALTIME = 0
|
||||||
@ -46,91 +46,40 @@ func semacreate() uintptr {
|
|||||||
func semasleep(ns int64) int32 {
|
func semasleep(ns int64) int32 {
|
||||||
_g_ := getg()
|
_g_ := getg()
|
||||||
|
|
||||||
// spin-mutex lock
|
// Compute sleep deadline.
|
||||||
for {
|
var tsp *timespec
|
||||||
if xchg(&_g_.m.waitsemalock, 1) == 0 {
|
if ns >= 0 {
|
||||||
break
|
var ts timespec
|
||||||
}
|
var nsec int32
|
||||||
osyield()
|
ns += nanotime()
|
||||||
|
ts.set_sec(timediv(ns, 1000000000, &nsec))
|
||||||
|
ts.set_nsec(nsec)
|
||||||
|
tsp = &ts
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// lock held
|
v := atomicload(&_g_.m.waitsemacount)
|
||||||
if _g_.m.waitsemacount == 0 {
|
if v > 0 {
|
||||||
// sleep until semaphore != 0 or timeout.
|
if cas(&_g_.m.waitsemacount, v, v-1) {
|
||||||
// thrsleep unlocks m.waitsemalock.
|
return 0 // semaphore acquired
|
||||||
if ns < 0 {
|
|
||||||
// TODO(jsing) - potential deadlock!
|
|
||||||
//
|
|
||||||
// There is a potential deadlock here since we
|
|
||||||
// have to release the waitsemalock mutex
|
|
||||||
// before we call lwp_park() to suspend the
|
|
||||||
// thread. This allows another thread to
|
|
||||||
// release the lock and call lwp_unpark()
|
|
||||||
// before the thread is actually suspended.
|
|
||||||
// If this occurs the current thread will end
|
|
||||||
// up sleeping indefinitely. Unfortunately
|
|
||||||
// the NetBSD kernel does not appear to provide
|
|
||||||
// a mechanism for unlocking the userspace
|
|
||||||
// mutex once the thread is actually parked.
|
|
||||||
atomicstore(&_g_.m.waitsemalock, 0)
|
|
||||||
lwp_park(nil, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
|
|
||||||
} else {
|
|
||||||
var ts timespec
|
|
||||||
var nsec int32
|
|
||||||
ns += nanotime()
|
|
||||||
ts.set_sec(timediv(ns, 1000000000, &nsec))
|
|
||||||
ts.set_nsec(nsec)
|
|
||||||
// TODO(jsing) - potential deadlock!
|
|
||||||
// See above for details.
|
|
||||||
atomicstore(&_g_.m.waitsemalock, 0)
|
|
||||||
lwp_park(&ts, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
|
|
||||||
}
|
|
||||||
// reacquire lock
|
|
||||||
for {
|
|
||||||
if xchg(&_g_.m.waitsemalock, 1) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
osyield()
|
|
||||||
}
|
}
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// lock held (again)
|
// Sleep until unparked by semawakeup or timeout.
|
||||||
if _g_.m.waitsemacount != 0 {
|
ret := lwp_park(tsp, 0, unsafe.Pointer(&_g_.m.waitsemacount), nil)
|
||||||
// semaphore is available.
|
if ret == _ETIMEDOUT {
|
||||||
_g_.m.waitsemacount--
|
return -1
|
||||||
// spin-mutex unlock
|
|
||||||
atomicstore(&_g_.m.waitsemalock, 0)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// semaphore not available.
|
|
||||||
// if there is a timeout, stop now.
|
|
||||||
// otherwise keep trying.
|
|
||||||
if ns >= 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// lock held but giving up
|
|
||||||
// spin-mutex unlock
|
|
||||||
atomicstore(&_g_.m.waitsemalock, 0)
|
|
||||||
return -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:nosplit
|
//go:nosplit
|
||||||
func semawakeup(mp *m) {
|
func semawakeup(mp *m) {
|
||||||
// spin-mutex lock
|
xadd(&mp.waitsemacount, 1)
|
||||||
for {
|
// From NetBSD's _lwp_unpark(2) manual:
|
||||||
if xchg(&mp.waitsemalock, 1) == 0 {
|
// "If the target LWP is not currently waiting, it will return
|
||||||
break
|
// immediately upon the next call to _lwp_park()."
|
||||||
}
|
|
||||||
osyield()
|
|
||||||
}
|
|
||||||
|
|
||||||
mp.waitsemacount++
|
|
||||||
// TODO(jsing) - potential deadlock, see semasleep() for details.
|
|
||||||
// Confirm that LWP is parked before unparking...
|
|
||||||
ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
|
ret := lwp_unpark(int32(mp.procid), unsafe.Pointer(&mp.waitsemacount))
|
||||||
if ret != 0 && ret != _ESRCH {
|
if ret != 0 && ret != _ESRCH {
|
||||||
// semawakeup can be called on signal stack.
|
// semawakeup can be called on signal stack.
|
||||||
@ -138,9 +87,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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user