Source file src/sync/rwmutex_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // GOMAXPROCS=10 go test
     6  
     7  package sync_test
     8  
     9  import (
    10  	"fmt"
    11  	"runtime"
    12  	. "sync"
    13  	"sync/atomic"
    14  	"testing"
    15  )
    16  
    17  // There is a modified copy of this file in runtime/rwmutex_test.go.
    18  // If you make any changes here, see if you should make them there.
    19  
    20  func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) {
    21  	m.RLock()
    22  	clocked <- true
    23  	<-cunlock
    24  	m.RUnlock()
    25  	cdone <- true
    26  }
    27  
    28  func doTestParallelReaders(numReaders, gomaxprocs int) {
    29  	runtime.GOMAXPROCS(gomaxprocs)
    30  	var m RWMutex
    31  	clocked := make(chan bool)
    32  	cunlock := make(chan bool)
    33  	cdone := make(chan bool)
    34  	for i := 0; i < numReaders; i++ {
    35  		go parallelReader(&m, clocked, cunlock, cdone)
    36  	}
    37  	// Wait for all parallel RLock()s to succeed.
    38  	for i := 0; i < numReaders; i++ {
    39  		<-clocked
    40  	}
    41  	for i := 0; i < numReaders; i++ {
    42  		cunlock <- true
    43  	}
    44  	// Wait for the goroutines to finish.
    45  	for i := 0; i < numReaders; i++ {
    46  		<-cdone
    47  	}
    48  }
    49  
    50  func TestParallelReaders(t *testing.T) {
    51  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
    52  	doTestParallelReaders(1, 4)
    53  	doTestParallelReaders(3, 4)
    54  	doTestParallelReaders(4, 2)
    55  }
    56  
    57  func reader(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
    58  	for i := 0; i < num_iterations; i++ {
    59  		rwm.RLock()
    60  		n := atomic.AddInt32(activity, 1)
    61  		if n < 1 || n >= 10000 {
    62  			rwm.RUnlock()
    63  			panic(fmt.Sprintf("wlock(%d)\n", n))
    64  		}
    65  		for i := 0; i < 100; i++ {
    66  		}
    67  		atomic.AddInt32(activity, -1)
    68  		rwm.RUnlock()
    69  	}
    70  	cdone <- true
    71  }
    72  
    73  func writer(rwm *RWMutex, num_iterations int, activity *int32, cdone chan bool) {
    74  	for i := 0; i < num_iterations; i++ {
    75  		rwm.Lock()
    76  		n := atomic.AddInt32(activity, 10000)
    77  		if n != 10000 {
    78  			rwm.Unlock()
    79  			panic(fmt.Sprintf("wlock(%d)\n", n))
    80  		}
    81  		for i := 0; i < 100; i++ {
    82  		}
    83  		atomic.AddInt32(activity, -10000)
    84  		rwm.Unlock()
    85  	}
    86  	cdone <- true
    87  }
    88  
    89  func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) {
    90  	runtime.GOMAXPROCS(gomaxprocs)
    91  	// Number of active readers + 10000 * number of active writers.
    92  	var activity int32
    93  	var rwm RWMutex
    94  	cdone := make(chan bool)
    95  	go writer(&rwm, num_iterations, &activity, cdone)
    96  	var i int
    97  	for i = 0; i < numReaders/2; i++ {
    98  		go reader(&rwm, num_iterations, &activity, cdone)
    99  	}
   100  	go writer(&rwm, num_iterations, &activity, cdone)
   101  	for ; i < numReaders; i++ {
   102  		go reader(&rwm, num_iterations, &activity, cdone)
   103  	}
   104  	// Wait for the 2 writers and all readers to finish.
   105  	for i := 0; i < 2+numReaders; i++ {
   106  		<-cdone
   107  	}
   108  }
   109  
   110  func TestRWMutex(t *testing.T) {
   111  	var m RWMutex
   112  
   113  	m.Lock()
   114  	if m.TryLock() {
   115  		t.Fatalf("TryLock succeeded with mutex locked")
   116  	}
   117  	if m.TryRLock() {
   118  		t.Fatalf("TryRLock succeeded with mutex locked")
   119  	}
   120  	m.Unlock()
   121  
   122  	if !m.TryLock() {
   123  		t.Fatalf("TryLock failed with mutex unlocked")
   124  	}
   125  	m.Unlock()
   126  
   127  	if !m.TryRLock() {
   128  		t.Fatalf("TryRLock failed with mutex unlocked")
   129  	}
   130  	if !m.TryRLock() {
   131  		t.Fatalf("TryRLock failed with mutex rlocked")
   132  	}
   133  	if m.TryLock() {
   134  		t.Fatalf("TryLock succeeded with mutex rlocked")
   135  	}
   136  	m.RUnlock()
   137  	m.RUnlock()
   138  
   139  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
   140  	n := 1000
   141  	if testing.Short() {
   142  		n = 5
   143  	}
   144  	HammerRWMutex(1, 1, n)
   145  	HammerRWMutex(1, 3, n)
   146  	HammerRWMutex(1, 10, n)
   147  	HammerRWMutex(4, 1, n)
   148  	HammerRWMutex(4, 3, n)
   149  	HammerRWMutex(4, 10, n)
   150  	HammerRWMutex(10, 1, n)
   151  	HammerRWMutex(10, 3, n)
   152  	HammerRWMutex(10, 10, n)
   153  	HammerRWMutex(10, 5, n)
   154  }
   155  
   156  func TestRLocker(t *testing.T) {
   157  	var wl RWMutex
   158  	var rl Locker
   159  	wlocked := make(chan bool, 1)
   160  	rlocked := make(chan bool, 1)
   161  	rl = wl.RLocker()
   162  	n := 10
   163  	go func() {
   164  		for i := 0; i < n; i++ {
   165  			rl.Lock()
   166  			rl.Lock()
   167  			rlocked <- true
   168  			wl.Lock()
   169  			wlocked <- true
   170  		}
   171  	}()
   172  	for i := 0; i < n; i++ {
   173  		<-rlocked
   174  		rl.Unlock()
   175  		select {
   176  		case <-wlocked:
   177  			t.Fatal("RLocker() didn't read-lock it")
   178  		default:
   179  		}
   180  		rl.Unlock()
   181  		<-wlocked
   182  		select {
   183  		case <-rlocked:
   184  			t.Fatal("RLocker() didn't respect the write lock")
   185  		default:
   186  		}
   187  		wl.Unlock()
   188  	}
   189  }
   190  
   191  func BenchmarkRWMutexUncontended(b *testing.B) {
   192  	type PaddedRWMutex struct {
   193  		RWMutex
   194  		pad [32]uint32
   195  	}
   196  	b.RunParallel(func(pb *testing.PB) {
   197  		var rwm PaddedRWMutex
   198  		for pb.Next() {
   199  			rwm.RLock()
   200  			rwm.RLock()
   201  			rwm.RUnlock()
   202  			rwm.RUnlock()
   203  			rwm.Lock()
   204  			rwm.Unlock()
   205  		}
   206  	})
   207  }
   208  
   209  func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) {
   210  	var rwm RWMutex
   211  	b.RunParallel(func(pb *testing.PB) {
   212  		foo := 0
   213  		for pb.Next() {
   214  			foo++
   215  			if foo%writeRatio == 0 {
   216  				rwm.Lock()
   217  				rwm.Unlock()
   218  			} else {
   219  				rwm.RLock()
   220  				for i := 0; i != localWork; i += 1 {
   221  					foo *= 2
   222  					foo /= 2
   223  				}
   224  				rwm.RUnlock()
   225  			}
   226  		}
   227  		_ = foo
   228  	})
   229  }
   230  
   231  func BenchmarkRWMutexWrite100(b *testing.B) {
   232  	benchmarkRWMutex(b, 0, 100)
   233  }
   234  
   235  func BenchmarkRWMutexWrite10(b *testing.B) {
   236  	benchmarkRWMutex(b, 0, 10)
   237  }
   238  
   239  func BenchmarkRWMutexWorkWrite100(b *testing.B) {
   240  	benchmarkRWMutex(b, 100, 100)
   241  }
   242  
   243  func BenchmarkRWMutexWorkWrite10(b *testing.B) {
   244  	benchmarkRWMutex(b, 100, 10)
   245  }
   246  

View as plain text