gcc/libgo/go/runtime/sema.go
Ian Lance Taylor f8d9fa9e80 libgo, compiler: Upgrade libgo to Go 1.4, except for runtime.
This upgrades all of libgo other than the runtime package to
the Go 1.4 release.  In Go 1.4 much of the runtime was
rewritten into Go.  Merging that code will take more time and
will not change the API, so I'm putting it off for now.

There are a few runtime changes anyhow, to accomodate other
packages that rely on minor modifications to the runtime
support.

The compiler changes slightly to add a one-bit flag to each
type descriptor kind that is stored directly in an interface,
which for gccgo is currently only pointer types.  Another
one-bit flag (gcprog) is reserved because it is used by the gc
compiler, but gccgo does not currently use it.

There is another error check in the compiler since I ran
across it during testing.

gotools/:
	* Makefile.am (go_cmd_go_files): Sort entries.  Add generate.go.
	* Makefile.in: Rebuild.

From-SVN: r219627
2015-01-15 00:27:56 +00:00

276 lines
5.6 KiB
Go

// Copyright 2009 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.
// Semaphore implementation exposed to Go.
// Intended use is provide a sleep and wakeup
// primitive that can be used in the contended case
// of other synchronization primitives.
// Thus it targets the same goal as Linux's futex,
// but it has much simpler semantics.
//
// That is, don't think of these as semaphores.
// Think of them as a way to implement sleep and wakeup
// such that every sleep is paired with a single wakeup,
// even if, due to races, the wakeup happens before the sleep.
//
// See Mullender and Cox, ``Semaphores in Plan 9,''
// http://swtch.com/semaphore.pdf
package runtime
import "unsafe"
// Asynchronous semaphore for sync.Mutex.
type semaRoot struct {
lock mutex
head *sudog
tail *sudog
nwait uint32 // Number of waiters. Read w/o the lock.
}
// Prime to not correlate with any user patterns.
const semTabSize = 251
var semtable [semTabSize]struct {
root semaRoot
pad [_CacheLineSize - unsafe.Sizeof(semaRoot{})]byte
}
// Called from sync/net packages.
func asyncsemacquire(addr *uint32) {
semacquire(addr, true)
}
func asyncsemrelease(addr *uint32) {
semrelease(addr)
}
// Called from runtime.
func semacquire(addr *uint32, profile bool) {
gp := getg()
if gp != gp.m.curg {
gothrow("semacquire not on the G stack")
}
// Easy case.
if cansemacquire(addr) {
return
}
// Harder case:
// increment waiter count
// try cansemacquire one more time, return if succeeded
// enqueue itself as a waiter
// sleep
// (waiter descriptor is dequeued by signaler)
s := acquireSudog()
root := semroot(addr)
t0 := int64(0)
s.releasetime = 0
if profile && blockprofilerate > 0 {
t0 = cputicks()
s.releasetime = -1
}
for {
lock(&root.lock)
// Add ourselves to nwait to disable "easy case" in semrelease.
xadd(&root.nwait, 1)
// Check cansemacquire to avoid missed wakeup.
if cansemacquire(addr) {
xadd(&root.nwait, -1)
unlock(&root.lock)
break
}
// Any semrelease after the cansemacquire knows we're waiting
// (we set nwait above), so go to sleep.
root.queue(addr, s)
goparkunlock(&root.lock, "semacquire")
if cansemacquire(addr) {
break
}
}
if s.releasetime > 0 {
blockevent(int64(s.releasetime)-t0, 3)
}
releaseSudog(s)
}
func semrelease(addr *uint32) {
root := semroot(addr)
xadd(addr, 1)
// Easy case: no waiters?
// This check must happen after the xadd, to avoid a missed wakeup
// (see loop in semacquire).
if atomicload(&root.nwait) == 0 {
return
}
// Harder case: search for a waiter and wake it.
lock(&root.lock)
if atomicload(&root.nwait) == 0 {
// The count is already consumed by another goroutine,
// so no need to wake up another goroutine.
unlock(&root.lock)
return
}
s := root.head
for ; s != nil; s = s.next {
if s.elem == unsafe.Pointer(addr) {
xadd(&root.nwait, -1)
root.dequeue(s)
break
}
}
unlock(&root.lock)
if s != nil {
if s.releasetime != 0 {
s.releasetime = cputicks()
}
goready(s.g)
}
}
func semroot(addr *uint32) *semaRoot {
return &semtable[(uintptr(unsafe.Pointer(addr))>>3)%semTabSize].root
}
func cansemacquire(addr *uint32) bool {
for {
v := atomicload(addr)
if v == 0 {
return false
}
if cas(addr, v, v-1) {
return true
}
}
}
func (root *semaRoot) queue(addr *uint32, s *sudog) {
s.g = getg()
s.elem = unsafe.Pointer(addr)
s.next = nil
s.prev = root.tail
if root.tail != nil {
root.tail.next = s
} else {
root.head = s
}
root.tail = s
}
func (root *semaRoot) dequeue(s *sudog) {
if s.next != nil {
s.next.prev = s.prev
} else {
root.tail = s.prev
}
if s.prev != nil {
s.prev.next = s.next
} else {
root.head = s.next
}
s.elem = nil
s.next = nil
s.prev = nil
}
// Synchronous semaphore for sync.Cond.
type syncSema struct {
lock mutex
head *sudog
tail *sudog
}
// Syncsemacquire waits for a pairing syncsemrelease on the same semaphore s.
func syncsemacquire(s *syncSema) {
lock(&s.lock)
if s.head != nil && s.head.nrelease > 0 {
// Have pending release, consume it.
var wake *sudog
s.head.nrelease--
if s.head.nrelease == 0 {
wake = s.head
s.head = wake.next
if s.head == nil {
s.tail = nil
}
}
unlock(&s.lock)
if wake != nil {
wake.next = nil
goready(wake.g)
}
} else {
// Enqueue itself.
w := acquireSudog()
w.g = getg()
w.nrelease = -1
w.next = nil
w.releasetime = 0
t0 := int64(0)
if blockprofilerate > 0 {
t0 = cputicks()
w.releasetime = -1
}
if s.tail == nil {
s.head = w
} else {
s.tail.next = w
}
s.tail = w
goparkunlock(&s.lock, "semacquire")
if t0 != 0 {
blockevent(int64(w.releasetime)-t0, 2)
}
releaseSudog(w)
}
}
// Syncsemrelease waits for n pairing syncsemacquire on the same semaphore s.
func syncsemrelease(s *syncSema, n uint32) {
lock(&s.lock)
for n > 0 && s.head != nil && s.head.nrelease < 0 {
// Have pending acquire, satisfy it.
wake := s.head
s.head = wake.next
if s.head == nil {
s.tail = nil
}
if wake.releasetime != 0 {
wake.releasetime = cputicks()
}
wake.next = nil
goready(wake.g)
n--
}
if n > 0 {
// enqueue itself
w := acquireSudog()
w.g = getg()
w.nrelease = int32(n)
w.next = nil
w.releasetime = 0
if s.tail == nil {
s.head = w
} else {
s.tail.next = w
}
s.tail = w
goparkunlock(&s.lock, "semarelease")
releaseSudog(w)
} else {
unlock(&s.lock)
}
}
func syncsemcheck(sz uintptr) {
if sz != unsafe.Sizeof(syncSema{}) {
print("runtime: bad syncSema size - sync=", sz, " runtime=", unsafe.Sizeof(syncSema{}), "\n")
gothrow("bad syncSema size")
}
}