| | |
| | |
| | |
| |
|
| | package sync_test |
| |
|
| | import ( |
| | "reflect" |
| | "runtime" |
| | . "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 |
| | } |
| | for n > 0 { |
| | select { |
| | case <-awake: |
| | t.Fatal("goroutine not asleep") |
| | default: |
| | } |
| | m.Lock() |
| | c.Signal() |
| | m.Unlock() |
| | <-awake |
| | 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 |
| | } |
| | 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() |
| | } |
| |
|
| | func TestRace(t *testing.T) { |
| | x := 0 |
| | c := NewCond(&Mutex{}) |
| | done := make(chan bool) |
| | go func() { |
| | c.L.Lock() |
| | x = 1 |
| | c.Wait() |
| | if x != 2 { |
| | t.Error("want 2") |
| | } |
| | x = 3 |
| | c.Signal() |
| | c.L.Unlock() |
| | done <- true |
| | }() |
| | go func() { |
| | c.L.Lock() |
| | for { |
| | if x == 1 { |
| | x = 2 |
| | c.Signal() |
| | break |
| | } |
| | c.L.Unlock() |
| | runtime.Gosched() |
| | c.L.Lock() |
| | } |
| | c.L.Unlock() |
| | done <- true |
| | }() |
| | go func() { |
| | c.L.Lock() |
| | for { |
| | if x == 2 { |
| | c.Wait() |
| | if x != 3 { |
| | t.Error("want 3") |
| | } |
| | break |
| | } |
| | if x == 3 { |
| | break |
| | } |
| | c.L.Unlock() |
| | runtime.Gosched() |
| | c.L.Lock() |
| | } |
| | c.L.Unlock() |
| | done <- true |
| | }() |
| | <-done |
| | <-done |
| | <-done |
| | } |
| |
|
| | func TestCondSignalStealing(t *testing.T) { |
| | for iters := 0; iters < 1000; iters++ { |
| | var m Mutex |
| | cond := NewCond(&m) |
| |
|
| | |
| | ch := make(chan struct{}) |
| | go func() { |
| | m.Lock() |
| | ch <- struct{}{} |
| | cond.Wait() |
| | m.Unlock() |
| |
|
| | ch <- struct{}{} |
| | }() |
| |
|
| | <-ch |
| | m.Lock() |
| | m.Unlock() |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | done := false |
| | go func() { |
| | cond.Broadcast() |
| | }() |
| |
|
| | go func() { |
| | m.Lock() |
| | for !done { |
| | cond.Wait() |
| | } |
| | m.Unlock() |
| | }() |
| |
|
| | |
| | <-ch |
| |
|
| | |
| | |
| | m.Lock() |
| | done = true |
| | m.Unlock() |
| | cond.Broadcast() |
| | } |
| | } |
| |
|
| | func TestCondCopy(t *testing.T) { |
| | defer func() { |
| | err := recover() |
| | if err == nil || err.(string) != "sync.Cond is copied" { |
| | t.Fatalf("got %v, expect sync.Cond is copied", err) |
| | } |
| | }() |
| | c := Cond{L: &Mutex{}} |
| | c.Signal() |
| | var c2 Cond |
| | reflect.ValueOf(&c2).Elem().Set(reflect.ValueOf(&c).Elem()) |
| | c2.Signal() |
| | } |
| |
|
| | func BenchmarkCond1(b *testing.B) { |
| | benchmarkCond(b, 1) |
| | } |
| |
|
| | func BenchmarkCond2(b *testing.B) { |
| | benchmarkCond(b, 2) |
| | } |
| |
|
| | func BenchmarkCond4(b *testing.B) { |
| | benchmarkCond(b, 4) |
| | } |
| |
|
| | func BenchmarkCond8(b *testing.B) { |
| | benchmarkCond(b, 8) |
| | } |
| |
|
| | func BenchmarkCond16(b *testing.B) { |
| | benchmarkCond(b, 16) |
| | } |
| |
|
| | func BenchmarkCond32(b *testing.B) { |
| | benchmarkCond(b, 32) |
| | } |
| |
|
| | func benchmarkCond(b *testing.B, waiters int) { |
| | c := NewCond(&Mutex{}) |
| | done := make(chan bool) |
| | id := 0 |
| |
|
| | for routine := 0; routine < waiters+1; routine++ { |
| | go func() { |
| | for i := 0; i < b.N; i++ { |
| | c.L.Lock() |
| | if id == -1 { |
| | c.L.Unlock() |
| | break |
| | } |
| | id++ |
| | if id == waiters+1 { |
| | id = 0 |
| | c.Broadcast() |
| | } else { |
| | c.Wait() |
| | } |
| | c.L.Unlock() |
| | } |
| | c.L.Lock() |
| | id = -1 |
| | c.Broadcast() |
| | c.L.Unlock() |
| | done <- true |
| | }() |
| | } |
| | for routine := 0; routine < waiters+1; routine++ { |
| | <-done |
| | } |
| | } |
| |
|