aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/runtime/lock_sema.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/runtime/lock_sema.go')
-rw-r--r--libgo/go/runtime/lock_sema.go281
1 files changed, 281 insertions, 0 deletions
diff --git a/libgo/go/runtime/lock_sema.go b/libgo/go/runtime/lock_sema.go
new file mode 100644
index 0000000..eaf938a
--- /dev/null
+++ b/libgo/go/runtime/lock_sema.go
@@ -0,0 +1,281 @@
+// Copyright 2011 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin nacl netbsd openbsd plan9 solaris windows
+
+package runtime
+
+import (
+ "runtime/internal/atomic"
+ "unsafe"
+)
+
+// For gccgo, while we still have C runtime code, use go:linkname to
+// rename some functions to themselves, so that the compiler will
+// export them.
+//
+//go:linkname lock runtime.lock
+//go:linkname unlock runtime.unlock
+//go:linkname noteclear runtime.noteclear
+//go:linkname notewakeup runtime.notewakeup
+//go:linkname notesleep runtime.notesleep
+//go:linkname notetsleep runtime.notetsleep
+//go:linkname notetsleepg runtime.notetsleepg
+
+// This implementation depends on OS-specific implementations of
+//
+// func semacreate(mp *m)
+// Create a semaphore for mp, if it does not already have one.
+//
+// func semasleep(ns int64) int32
+// If ns < 0, acquire m's semaphore and return 0.
+// If ns >= 0, try to acquire m's semaphore for at most ns nanoseconds.
+// Return 0 if the semaphore was acquired, -1 if interrupted or timed out.
+//
+// func semawakeup(mp *m)
+// Wake up mp, which is or will soon be sleeping on its semaphore.
+//
+const (
+ mutex_locked uintptr = 1
+
+ active_spin = 4
+ active_spin_cnt = 30
+ passive_spin = 1
+)
+
+func lock(l *mutex) {
+ gp := getg()
+ if gp.m.locks < 0 {
+ throw("runtime·lock: lock count")
+ }
+ gp.m.locks++
+
+ // Speculative grab for lock.
+ if atomic.Casuintptr(&l.key, 0, mutex_locked) {
+ return
+ }
+ semacreate(gp.m)
+
+ // On uniprocessor's, no point spinning.
+ // On multiprocessors, spin for ACTIVE_SPIN attempts.
+ spin := 0
+ if ncpu > 1 {
+ spin = active_spin
+ }
+Loop:
+ for i := 0; ; i++ {
+ v := atomic.Loaduintptr(&l.key)
+ if v&mutex_locked == 0 {
+ // Unlocked. Try to lock.
+ if atomic.Casuintptr(&l.key, v, v|mutex_locked) {
+ return
+ }
+ i = 0
+ }
+ if i < spin {
+ procyield(active_spin_cnt)
+ } else if i < spin+passive_spin {
+ osyield()
+ } else {
+ // Someone else has it.
+ // l->waitm points to a linked list of M's waiting
+ // for this lock, chained through m->nextwaitm.
+ // Queue this M.
+ for {
+ gp.m.nextwaitm = v &^ mutex_locked
+ if atomic.Casuintptr(&l.key, v, uintptr(unsafe.Pointer(gp.m))|mutex_locked) {
+ break
+ }
+ v = atomic.Loaduintptr(&l.key)
+ if v&mutex_locked == 0 {
+ continue Loop
+ }
+ }
+ if v&mutex_locked != 0 {
+ // Queued. Wait.
+ semasleep(-1)
+ i = 0
+ }
+ }
+ }
+}
+
+//go:nowritebarrier
+// We might not be holding a p in this code.
+func unlock(l *mutex) {
+ gp := getg()
+ var mp *m
+ for {
+ v := atomic.Loaduintptr(&l.key)
+ if v == mutex_locked {
+ if atomic.Casuintptr(&l.key, mutex_locked, 0) {
+ break
+ }
+ } else {
+ // Other M's are waiting for the lock.
+ // Dequeue an M.
+ mp = (*m)(unsafe.Pointer(v &^ mutex_locked))
+ if atomic.Casuintptr(&l.key, v, mp.nextwaitm) {
+ // Dequeued an M. Wake it.
+ semawakeup(mp)
+ break
+ }
+ }
+ }
+ gp.m.locks--
+ if gp.m.locks < 0 {
+ throw("runtime·unlock: lock count")
+ }
+ // if gp.m.locks == 0 && gp.preempt { // restore the preemption request in case we've cleared it in newstack
+ // gp.stackguard0 = stackPreempt
+ // }
+}
+
+// One-time notifications.
+func noteclear(n *note) {
+ n.key = 0
+}
+
+func notewakeup(n *note) {
+ var v uintptr
+ for {
+ v = atomic.Loaduintptr(&n.key)
+ if atomic.Casuintptr(&n.key, v, mutex_locked) {
+ break
+ }
+ }
+
+ // Successfully set waitm to locked.
+ // What was it before?
+ switch {
+ case v == 0:
+ // Nothing was waiting. Done.
+ case v == mutex_locked:
+ // Two notewakeups! Not allowed.
+ throw("notewakeup - double wakeup")
+ default:
+ // Must be the waiting m. Wake it up.
+ semawakeup((*m)(unsafe.Pointer(v)))
+ }
+}
+
+func notesleep(n *note) {
+ gp := getg()
+
+ // Currently OK to sleep in non-g0 for gccgo. It happens in
+ // stoptheworld because we have not implemented preemption.
+ // if gp != gp.m.g0 {
+ // throw("notesleep not on g0")
+ // }
+
+ semacreate(gp.m)
+ if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
+ // Must be locked (got wakeup).
+ if n.key != mutex_locked {
+ throw("notesleep - waitm out of sync")
+ }
+ return
+ }
+ // Queued. Sleep.
+ gp.m.blocked = true
+ semasleep(-1)
+ gp.m.blocked = false
+}
+
+//go:nosplit
+func notetsleep_internal(n *note, ns int64, gp *g, deadline int64) bool {
+ // gp and deadline are logically local variables, but they are written
+ // as parameters so that the stack space they require is charged
+ // to the caller.
+ // This reduces the nosplit footprint of notetsleep_internal.
+ gp = getg()
+
+ // Register for wakeup on n->waitm.
+ if !atomic.Casuintptr(&n.key, 0, uintptr(unsafe.Pointer(gp.m))) {
+ // Must be locked (got wakeup).
+ if n.key != mutex_locked {
+ throw("notetsleep - waitm out of sync")
+ }
+ return true
+ }
+ if ns < 0 {
+ // Queued. Sleep.
+ gp.m.blocked = true
+ semasleep(-1)
+ gp.m.blocked = false
+ return true
+ }
+
+ deadline = nanotime() + ns
+ for {
+ // Registered. Sleep.
+ gp.m.blocked = true
+ if semasleep(ns) >= 0 {
+ gp.m.blocked = false
+ // Acquired semaphore, semawakeup unregistered us.
+ // Done.
+ return true
+ }
+ gp.m.blocked = false
+ // Interrupted or timed out. Still registered. Semaphore not acquired.
+ ns = deadline - nanotime()
+ if ns <= 0 {
+ break
+ }
+ // Deadline hasn't arrived. Keep sleeping.
+ }
+
+ // Deadline arrived. Still registered. Semaphore not acquired.
+ // Want to give up and return, but have to unregister first,
+ // so that any notewakeup racing with the return does not
+ // try to grant us the semaphore when we don't expect it.
+ for {
+ v := atomic.Loaduintptr(&n.key)
+ switch v {
+ case uintptr(unsafe.Pointer(gp.m)):
+ // No wakeup yet; unregister if possible.
+ if atomic.Casuintptr(&n.key, v, 0) {
+ return false
+ }
+ case mutex_locked:
+ // Wakeup happened so semaphore is available.
+ // Grab it to avoid getting out of sync.
+ gp.m.blocked = true
+ if semasleep(-1) < 0 {
+ throw("runtime: unable to acquire - semaphore out of sync")
+ }
+ gp.m.blocked = false
+ return true
+ default:
+ throw("runtime: unexpected waitm - semaphore out of sync")
+ }
+ }
+}
+
+func notetsleep(n *note, ns int64) bool {
+ gp := getg()
+
+ // Currently OK to sleep in non-g0 for gccgo. It happens in
+ // stoptheworld because we have not implemented preemption.
+ // if gp != gp.m.g0 && gp.m.preemptoff != "" {
+ // throw("notetsleep not on g0")
+ // }
+
+ semacreate(gp.m)
+ return notetsleep_internal(n, ns, nil, 0)
+}
+
+// same as runtime·notetsleep, but called on user g (not g0)
+// calls only nosplit functions between entersyscallblock/exitsyscall
+func notetsleepg(n *note, ns int64) bool {
+ gp := getg()
+ if gp == gp.m.g0 {
+ throw("notetsleepg on g0")
+ }
+ semacreate(gp.m)
+ entersyscallblock(0)
+ ok := notetsleep_internal(n, ns, nil, 0)
+ exitsyscall(0)
+ return ok
+}