| | |
| | |
| | |
| |
|
| | |
| |
|
| | package sync_test |
| |
|
| | import ( |
| | "fmt" |
| | "internal/testenv" |
| | "os" |
| | "os/exec" |
| | "runtime" |
| | "strings" |
| | . "sync" |
| | "testing" |
| | "time" |
| | ) |
| |
|
| | func HammerSemaphore(s *uint32, loops int, cdone chan bool) { |
| | for i := 0; i < loops; i++ { |
| | Runtime_Semacquire(s) |
| | Runtime_Semrelease(s, false, 0) |
| | } |
| | cdone <- true |
| | } |
| |
|
| | func TestSemaphore(t *testing.T) { |
| | s := new(uint32) |
| | *s = 1 |
| | c := make(chan bool) |
| | for i := 0; i < 10; i++ { |
| | go HammerSemaphore(s, 1000, c) |
| | } |
| | for i := 0; i < 10; i++ { |
| | <-c |
| | } |
| | } |
| |
|
| | func BenchmarkUncontendedSemaphore(b *testing.B) { |
| | s := new(uint32) |
| | *s = 1 |
| | HammerSemaphore(s, b.N, make(chan bool, 2)) |
| | } |
| |
|
| | func BenchmarkContendedSemaphore(b *testing.B) { |
| | b.StopTimer() |
| | s := new(uint32) |
| | *s = 1 |
| | c := make(chan bool) |
| | defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) |
| | b.StartTimer() |
| |
|
| | go HammerSemaphore(s, b.N/2, c) |
| | go HammerSemaphore(s, b.N/2, c) |
| | <-c |
| | <-c |
| | } |
| |
|
| | func HammerMutex(m *Mutex, loops int, cdone chan bool) { |
| | for i := 0; i < loops; i++ { |
| | if i%3 == 0 { |
| | if m.TryLock() { |
| | m.Unlock() |
| | } |
| | continue |
| | } |
| | m.Lock() |
| | m.Unlock() |
| | } |
| | cdone <- true |
| | } |
| |
|
| | func TestMutex(t *testing.T) { |
| | if n := runtime.SetMutexProfileFraction(1); n != 0 { |
| | t.Logf("got mutexrate %d expected 0", n) |
| | } |
| | defer runtime.SetMutexProfileFraction(0) |
| |
|
| | m := new(Mutex) |
| |
|
| | m.Lock() |
| | if m.TryLock() { |
| | t.Fatalf("TryLock succeeded with mutex locked") |
| | } |
| | m.Unlock() |
| | if !m.TryLock() { |
| | t.Fatalf("TryLock failed with mutex unlocked") |
| | } |
| | m.Unlock() |
| |
|
| | c := make(chan bool) |
| | for i := 0; i < 10; i++ { |
| | go HammerMutex(m, 1000, c) |
| | } |
| | for i := 0; i < 10; i++ { |
| | <-c |
| | } |
| | } |
| |
|
| | var misuseTests = []struct { |
| | name string |
| | f func() |
| | }{ |
| | { |
| | "Mutex.Unlock", |
| | func() { |
| | var mu Mutex |
| | mu.Unlock() |
| | }, |
| | }, |
| | { |
| | "Mutex.Unlock2", |
| | func() { |
| | var mu Mutex |
| | mu.Lock() |
| | mu.Unlock() |
| | mu.Unlock() |
| | }, |
| | }, |
| | { |
| | "RWMutex.Unlock", |
| | func() { |
| | var mu RWMutex |
| | mu.Unlock() |
| | }, |
| | }, |
| | { |
| | "RWMutex.Unlock2", |
| | func() { |
| | var mu RWMutex |
| | mu.RLock() |
| | mu.Unlock() |
| | }, |
| | }, |
| | { |
| | "RWMutex.Unlock3", |
| | func() { |
| | var mu RWMutex |
| | mu.Lock() |
| | mu.Unlock() |
| | mu.Unlock() |
| | }, |
| | }, |
| | { |
| | "RWMutex.RUnlock", |
| | func() { |
| | var mu RWMutex |
| | mu.RUnlock() |
| | }, |
| | }, |
| | { |
| | "RWMutex.RUnlock2", |
| | func() { |
| | var mu RWMutex |
| | mu.Lock() |
| | mu.RUnlock() |
| | }, |
| | }, |
| | { |
| | "RWMutex.RUnlock3", |
| | func() { |
| | var mu RWMutex |
| | mu.RLock() |
| | mu.RUnlock() |
| | mu.RUnlock() |
| | }, |
| | }, |
| | } |
| |
|
| | func init() { |
| | if len(os.Args) == 3 && os.Args[1] == "TESTMISUSE" { |
| | for _, test := range misuseTests { |
| | if test.name == os.Args[2] { |
| | func() { |
| | defer func() { recover() }() |
| | test.f() |
| | }() |
| | fmt.Printf("test completed\n") |
| | os.Exit(0) |
| | } |
| | } |
| | fmt.Printf("unknown test\n") |
| | os.Exit(0) |
| | } |
| | } |
| |
|
| | func TestMutexMisuse(t *testing.T) { |
| | for _, test := range misuseTests { |
| | out, err := exec.Command(testenv.Executable(t), "TESTMISUSE", test.name).CombinedOutput() |
| | if err == nil || !strings.Contains(string(out), "unlocked") { |
| | t.Errorf("%s: did not find failure with message about unlocked lock: %s\n%s\n", test.name, err, out) |
| | } |
| | } |
| | } |
| |
|
| | func TestMutexFairness(t *testing.T) { |
| | var mu Mutex |
| | stop := make(chan bool) |
| | defer close(stop) |
| | go func() { |
| | for { |
| | mu.Lock() |
| | time.Sleep(100 * time.Microsecond) |
| | mu.Unlock() |
| | select { |
| | case <-stop: |
| | return |
| | default: |
| | } |
| | } |
| | }() |
| | done := make(chan bool, 1) |
| | go func() { |
| | for i := 0; i < 10; i++ { |
| | time.Sleep(100 * time.Microsecond) |
| | mu.Lock() |
| | mu.Unlock() |
| | } |
| | done <- true |
| | }() |
| | select { |
| | case <-done: |
| | case <-time.After(10 * time.Second): |
| | t.Fatalf("can't acquire Mutex in 10 seconds") |
| | } |
| | } |
| |
|
| | func BenchmarkMutexUncontended(b *testing.B) { |
| | type PaddedMutex struct { |
| | Mutex |
| | pad [128]uint8 |
| | } |
| | b.RunParallel(func(pb *testing.PB) { |
| | var mu PaddedMutex |
| | for pb.Next() { |
| | mu.Lock() |
| | mu.Unlock() |
| | } |
| | }) |
| | } |
| |
|
| | func benchmarkMutex(b *testing.B, slack, work bool) { |
| | var mu Mutex |
| | if slack { |
| | b.SetParallelism(10) |
| | } |
| | b.RunParallel(func(pb *testing.PB) { |
| | foo := 0 |
| | for pb.Next() { |
| | mu.Lock() |
| | mu.Unlock() |
| | if work { |
| | for i := 0; i < 100; i++ { |
| | foo *= 2 |
| | foo /= 2 |
| | } |
| | } |
| | } |
| | _ = foo |
| | }) |
| | } |
| |
|
| | func BenchmarkMutex(b *testing.B) { |
| | benchmarkMutex(b, false, false) |
| | } |
| |
|
| | func BenchmarkMutexSlack(b *testing.B) { |
| | benchmarkMutex(b, true, false) |
| | } |
| |
|
| | func BenchmarkMutexWork(b *testing.B) { |
| | benchmarkMutex(b, false, true) |
| | } |
| |
|
| | func BenchmarkMutexWorkSlack(b *testing.B) { |
| | benchmarkMutex(b, true, true) |
| | } |
| |
|
| | func BenchmarkMutexNoSpin(b *testing.B) { |
| | |
| | |
| | |
| | |
| | |
| | |
| | var m Mutex |
| | var acc0, acc1 uint64 |
| | b.SetParallelism(4) |
| | b.RunParallel(func(pb *testing.PB) { |
| | c := make(chan bool) |
| | var data [4 << 10]uint64 |
| | for i := 0; pb.Next(); i++ { |
| | if i%4 == 0 { |
| | m.Lock() |
| | acc0 -= 100 |
| | acc1 += 100 |
| | m.Unlock() |
| | } else { |
| | for i := 0; i < len(data); i += 4 { |
| | data[i]++ |
| | } |
| | |
| | |
| | go func() { |
| | c <- true |
| | }() |
| | <-c |
| | } |
| | } |
| | }) |
| | } |
| |
|
| | func BenchmarkMutexSpin(b *testing.B) { |
| | |
| | |
| | |
| | |
| | var m Mutex |
| | var acc0, acc1 uint64 |
| | b.RunParallel(func(pb *testing.PB) { |
| | var data [16 << 10]uint64 |
| | for i := 0; pb.Next(); i++ { |
| | m.Lock() |
| | acc0 -= 100 |
| | acc1 += 100 |
| | m.Unlock() |
| | for i := 0; i < len(data); i += 4 { |
| | data[i]++ |
| | } |
| | } |
| | }) |
| | } |
| |
|