summaryrefslogtreecommitdiff
path: root/src/pkg/sync
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/sync')
-rw-r--r--src/pkg/sync/atomic/asm_arm.s9
-rw-r--r--src/pkg/sync/cond.go69
-rw-r--r--src/pkg/sync/cond_test.go27
-rw-r--r--src/pkg/sync/rwmutex_test.go2
4 files changed, 81 insertions, 26 deletions
diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s
index 3363bbcf1..95e2f5be4 100644
--- a/src/pkg/sync/atomic/asm_arm.s
+++ b/src/pkg/sync/atomic/asm_arm.s
@@ -87,11 +87,14 @@ add64loop:
// which will make uses of the 64-bit atomic operations loop forever.
// If things are working, set okLDREXD to avoid future checks.
// https://bugs.launchpad.net/qemu/+bug/670883.
-TEXT check64<>(SB),7,$8
+TEXT check64<>(SB),7,$16
MOVW $10, R1
+ // 8-aligned stack address scratch space.
+ MOVW $8(R13), R5
+ AND $~7, R5
loop:
- LDREXD (SP), R2
- STREXD R2, (SP), R0
+ LDREXD (R5), R2
+ STREXD R2, (R5), R0
CMP $0, R0
BEQ ok
SUB $1, R1
diff --git a/src/pkg/sync/cond.go b/src/pkg/sync/cond.go
index ea48f2e7a..75494b535 100644
--- a/src/pkg/sync/cond.go
+++ b/src/pkg/sync/cond.go
@@ -14,10 +14,26 @@ import "runtime"
// which must be held when changing the condition and
// when calling the Wait method.
type Cond struct {
- L Locker // held while observing or changing the condition
- m Mutex // held to avoid internal races
- waiters int // number of goroutines blocked on Wait
- sema *uint32
+ L Locker // held while observing or changing the condition
+ m Mutex // held to avoid internal races
+
+ // We must be careful to make sure that when Signal
+ // releases a semaphore, the corresponding acquire is
+ // executed by a goroutine that was already waiting at
+ // the time of the call to Signal, not one that arrived later.
+ // To ensure this, we segment waiting goroutines into
+ // generations punctuated by calls to Signal. Each call to
+ // Signal begins another generation if there are no goroutines
+ // left in older generations for it to wake. Because of this
+ // optimization (only begin another generation if there
+ // are no older goroutines left), we only need to keep track
+ // of the two most recent generations, which we call old
+ // and new.
+ oldWaiters int // number of waiters in old generation...
+ oldSema *uint32 // ... waiting on this semaphore
+
+ newWaiters int // number of waiters in new generation...
+ newSema *uint32 // ... waiting on this semaphore
}
// NewCond returns a new Cond with Locker l.
@@ -42,11 +58,11 @@ func NewCond(l Locker) *Cond {
//
func (c *Cond) Wait() {
c.m.Lock()
- if c.sema == nil {
- c.sema = new(uint32)
+ if c.newSema == nil {
+ c.newSema = new(uint32)
}
- s := c.sema
- c.waiters++
+ s := c.newSema
+ c.newWaiters++
c.m.Unlock()
c.L.Unlock()
runtime.Semacquire(s)
@@ -59,9 +75,16 @@ func (c *Cond) Wait() {
// during the call.
func (c *Cond) Signal() {
c.m.Lock()
- if c.waiters > 0 {
- c.waiters--
- runtime.Semrelease(c.sema)
+ if c.oldWaiters == 0 && c.newWaiters > 0 {
+ // Retire old generation; rename new to old.
+ c.oldWaiters = c.newWaiters
+ c.oldSema = c.newSema
+ c.newWaiters = 0
+ c.newSema = nil
+ }
+ if c.oldWaiters > 0 {
+ c.oldWaiters--
+ runtime.Semrelease(c.oldSema)
}
c.m.Unlock()
}
@@ -72,19 +95,19 @@ func (c *Cond) Signal() {
// during the call.
func (c *Cond) Broadcast() {
c.m.Lock()
- if c.waiters > 0 {
- s := c.sema
- n := c.waiters
- for i := 0; i < n; i++ {
- runtime.Semrelease(s)
+ // Wake both generations.
+ if c.oldWaiters > 0 {
+ for i := 0; i < c.oldWaiters; i++ {
+ runtime.Semrelease(c.oldSema)
+ }
+ c.oldWaiters = 0
+ }
+ if c.newWaiters > 0 {
+ for i := 0; i < c.newWaiters; i++ {
+ runtime.Semrelease(c.newSema)
}
- // We just issued n wakeups via the semaphore s.
- // To ensure that they wake up the existing waiters
- // and not waiters that arrive after Broadcast returns,
- // clear c.sema. The next operation will allocate
- // a new one.
- c.sema = nil
- c.waiters = 0
+ c.newWaiters = 0
+ c.newSema = nil
}
c.m.Unlock()
}
diff --git a/src/pkg/sync/cond_test.go b/src/pkg/sync/cond_test.go
index 846f98bf3..cefacb184 100644
--- a/src/pkg/sync/cond_test.go
+++ b/src/pkg/sync/cond_test.go
@@ -46,6 +46,33 @@ func TestCondSignal(t *testing.T) {
c.Signal()
}
+func TestCondSignalGenerations(t *testing.T) {
+ var m Mutex
+ c := NewCond(&m)
+ n := 100
+ running := make(chan bool, n)
+ awake := make(chan int, n)
+ for i := 0; i < n; i++ {
+ go func(i int) {
+ m.Lock()
+ running <- true
+ c.Wait()
+ awake <- i
+ m.Unlock()
+ }(i)
+ if i > 0 {
+ a := <-awake
+ if a != i-1 {
+ t.Fatalf("wrong goroutine woke up: want %d, got %d", i-1, a)
+ }
+ }
+ <-running
+ m.Lock()
+ c.Signal()
+ m.Unlock()
+ }
+}
+
func TestCondBroadcast(t *testing.T) {
var m Mutex
c := NewCond(&m)
diff --git a/src/pkg/sync/rwmutex_test.go b/src/pkg/sync/rwmutex_test.go
index 9fb89f8e8..0480a6601 100644
--- a/src/pkg/sync/rwmutex_test.go
+++ b/src/pkg/sync/rwmutex_test.go
@@ -45,6 +45,7 @@ func doTestParallelReaders(numReaders, gomaxprocs int) {
}
func TestParallelReaders(t *testing.T) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
doTestParallelReaders(1, 4)
doTestParallelReaders(3, 4)
doTestParallelReaders(4, 2)
@@ -102,6 +103,7 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
}
func TestRWMutex(t *testing.T) {
+ defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
n := 1000
if testing.Short() {
n = 5