...
Run Format

Source file src/sync/rwmutex_test.go

Documentation: sync

  // Copyright 2009 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.
  
  // GOMAXPROCS=10 go test
  
  package sync_test
  
  import (
  	"fmt"
  	"runtime"
  	. "sync"
  	"sync/atomic"
  	"testing"
  )
  
  // There is a modified copy of this file in runtime/rwmutex_test.go.
  // If you make any changes here, see if you should make them there.
  
  func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) {
  	m.RLock()
  	clocked <- true
  	<-cunlock
  	m.RUnlock()
  	cdone <- true
  }
  
  func doTestParallelReaders(numReaders, gomaxprocs int) {
  	runtime.GOMAXPROCS(gomaxprocs)
  	var m RWMutex
  	clocked := make(chan bool)
  	cunlock := make(chan bool)
  	cdone := make(chan bool)
  	for i := 0; i < numReaders; i++ {
  		go parallelReader(&m, clocked, cunlock, cdone)
  	}
  	// Wait for all parallel RLock()s to succeed.
  	for i := 0; i < numReaders; i++ {
  		<-clocked
  	}
  	for i := 0; i < numReaders; i++ {
  		cunlock <- true
  	}
  	// Wait for the goroutines to finish.
  	for i := 0; i < numReaders; i++ {
  		<-cdone
  	}
  }
  
  func TestParallelReaders(t *testing.T) {
  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
  	doTestParallelReaders(1, 4)
  	doTestParallelReaders(3, 4)
  	doTestParallelReaders(4, 2)
  }
  
  func reader(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
  	for i := 0; i < num_iterations; i++ {
  		rwm.RLock()
  		n := atomic.AddInt32(activity, 1)
  		if n < 1 || n >= 10000 {
  			panic(fmt.Sprintf("wlock(%d)\n", n))
  		}
  		for i := 0; i < 100; i++ {
  		}
  		atomic.AddInt32(activity, -1)
  		rwm.RUnlock()
  	}
  	cdone <- true
  }
  
  func writer(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
  	for i := 0; i < num_iterations; i++ {
  		rwm.Lock()
  		n := atomic.AddInt32(activity, 10000)
  		if n != 10000 {
  			panic(fmt.Sprintf("wlock(%d)\n", n))
  		}
  		for i := 0; i < 100; i++ {
  		}
  		atomic.AddInt32(activity, -10000)
  		rwm.Unlock()
  	}
  	cdone <- true
  }
  
  func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
  	runtime.GOMAXPROCS(gomaxprocs)
  	// Number of active readers + 10000 * number of active writers.
  	var activity int32
  	var rwm RWMutex
  	cdone := make(chan bool)
  	go writer(&rwm, num_iterations, &activity, cdone)
  	var i int
  	for i = 0; i < numReaders/2; i++ {
  		go reader(&rwm, num_iterations, &activity, cdone)
  	}
  	go writer(&rwm, num_iterations, &activity, cdone)
  	for ; i < numReaders; i++ {
  		go reader(&rwm, num_iterations, &activity, cdone)
  	}
  	// Wait for the 2 writers and all readers to finish.
  	for i := 0; i < 2+numReaders; i++ {
  		<-cdone
  	}
  }
  
  func TestRWMutex(t *testing.T) {
  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
  	n := 1000
  	if testing.Short() {
  		n = 5
  	}
  	HammerRWMutex(1, 1, n)
  	HammerRWMutex(1, 3, n)
  	HammerRWMutex(1, 10, n)
  	HammerRWMutex(4, 1, n)
  	HammerRWMutex(4, 3, n)
  	HammerRWMutex(4, 10, n)
  	HammerRWMutex(10, 1, n)
  	HammerRWMutex(10, 3, n)
  	HammerRWMutex(10, 10, n)
  	HammerRWMutex(10, 5, n)
  }
  
  func TestRLocker(t *testing.T) {
  	var wl RWMutex
  	var rl Locker
  	wlocked := make(chan bool, 1)
  	rlocked := make(chan bool, 1)
  	rl = wl.RLocker()
  	n := 10
  	go func() {
  		for i := 0; i < n; i++ {
  			rl.Lock()
  			rl.Lock()
  			rlocked <- true
  			wl.Lock()
  			wlocked <- true
  		}
  	}()
  	for i := 0; i < n; i++ {
  		<-rlocked
  		rl.Unlock()
  		select {
  		case <-wlocked:
  			t.Fatal("RLocker() didn't read-lock it")
  		default:
  		}
  		rl.Unlock()
  		<-wlocked
  		select {
  		case <-rlocked:
  			t.Fatal("RLocker() didn't respect the write lock")
  		default:
  		}
  		wl.Unlock()
  	}
  }
  
  func BenchmarkRWMutexUncontended(b *testing.B) {
  	type PaddedRWMutex struct {
  		RWMutex
  		pad [32]uint32
  	}
  	b.RunParallel(func(pb *testing.PB) {
  		var rwm PaddedRWMutex
  		for pb.Next() {
  			rwm.RLock()
  			rwm.RLock()
  			rwm.RUnlock()
  			rwm.RUnlock()
  			rwm.Lock()
  			rwm.Unlock()
  		}
  	})
  }
  
  func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) {
  	var rwm RWMutex
  	b.RunParallel(func(pb *testing.PB) {
  		foo := 0
  		for pb.Next() {
  			foo++
  			if foo%writeRatio == 0 {
  				rwm.Lock()
  				rwm.Unlock()
  			} else {
  				rwm.RLock()
  				for i := 0; i != localWork; i += 1 {
  					foo *= 2
  					foo /= 2
  				}
  				rwm.RUnlock()
  			}
  		}
  		_ = foo
  	})
  }
  
  func BenchmarkRWMutexWrite100(b *testing.B) {
  	benchmarkRWMutex(b, 0, 100)
  }
  
  func BenchmarkRWMutexWrite10(b *testing.B) {
  	benchmarkRWMutex(b, 0, 10)
  }
  
  func BenchmarkRWMutexWorkWrite100(b *testing.B) {
  	benchmarkRWMutex(b, 100, 100)
  }
  
  func BenchmarkRWMutexWorkWrite10(b *testing.B) {
  	benchmarkRWMutex(b, 100, 10)
  }
  

View as plain text