mirror of
https://github.com/golang/go
synced 2024-10-05 02:21:22 -06:00
17bfa32fde
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
127 lines
2.1 KiB
Go
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()
|
|
}
|