1
0
mirror of https://github.com/golang/go synced 2024-10-05 02:21:22 -06:00
go/src/pkg/sync/cond_test.go
Gustavo Niemeyer 17bfa32fde sync: always wake up previously sleeping goroutines on Cond.Signal
This changes the internal implementation of Cond so that
it uses two generations of waiters.  This enables Signal
to guarantee that it will only wake up waiters that are
currently sleeping at the call time.

Fixes #1648.

R=dvyukov, gustavo, rsc
CC=golang-dev
https://golang.org/cl/4524083
2011-06-01 20:30:42 -03:00

127 lines
2.1 KiB
Go

// 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.
package sync_test
import (
. "sync"
"testing"
)
func TestCondSignal(t *testing.T) {
var m Mutex
c := NewCond(&m)
n := 2
running := make(chan bool, n)
awake := make(chan bool, n)
for i := 0; i < n; i++ {
go func() {
m.Lock()
running <- true
c.Wait()
awake <- true
m.Unlock()
}()
}
for i := 0; i < n; i++ {
<-running // Wait for everyone to run.
}
for n > 0 {
select {
case <-awake:
t.Fatal("goroutine not asleep")
default:
}
m.Lock()
c.Signal()
m.Unlock()
<-awake // Will deadlock if no goroutine wakes up
select {
case <-awake:
t.Fatal("too many goroutines awake")
default:
}
n--
}
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)
n := 200
running := make(chan int, n)
awake := make(chan int, n)
exit := false
for i := 0; i < n; i++ {
go func(g int) {
m.Lock()
for !exit {
running <- g
c.Wait()
awake <- g
}
m.Unlock()
}(i)
}
for i := 0; i < n; i++ {
for i := 0; i < n; i++ {
<-running // Will deadlock unless n are running.
}
if i == n-1 {
m.Lock()
exit = true
m.Unlock()
}
select {
case <-awake:
t.Fatal("goroutine not asleep")
default:
}
m.Lock()
c.Broadcast()
m.Unlock()
seen := make([]bool, n)
for i := 0; i < n; i++ {
g := <-awake
if seen[g] {
t.Fatal("goroutine woke up twice")
}
seen[g] = true
}
}
select {
case <-running:
t.Fatal("goroutine did not exit")
default:
}
c.Broadcast()
}