github.com/dmmcquay/sia@v1.3.1-0.20180712220038-9f8d535311b9/sync/tryrwmutex_test.go (about)

     1  package sync
     2  
     3  import (
     4  	"runtime"
     5  	"sync"
     6  	"testing"
     7  )
     8  
     9  // TestTryRWMutexBasicMutex verifies that Lock and Unlock work the same as a
    10  // normal mutex would.
    11  func TestTryRWMutexBasicMutex(t *testing.T) {
    12  	// Check that two calls to lock will execute in the correct order.
    13  	var tm TryRWMutex
    14  	var data int
    15  	tm.Lock()
    16  	go func() {
    17  		data = 15
    18  		tm.Unlock()
    19  	}()
    20  	tm.Lock()
    21  	if data != 15 {
    22  		t.Error("Locking did not safely protect the data")
    23  	}
    24  	tm.Unlock()
    25  }
    26  
    27  // TestTryRWMutexConcurrentLocking checks that doing lots of concurrent locks
    28  // is handled as expected.
    29  func TestTryRWMutexConcurrentLocking(t *testing.T) {
    30  	if testing.Short() {
    31  		t.SkipNow()
    32  	}
    33  
    34  	// Try executing multiple additions concurrently.
    35  	var tm TryRWMutex
    36  	var data int
    37  	var wg sync.WaitGroup
    38  	for i := 0; i < 250; i++ {
    39  		wg.Add(1)
    40  		go func() {
    41  			tm.Lock()
    42  			data++
    43  			tm.Unlock()
    44  			wg.Done()
    45  		}()
    46  	}
    47  	wg.Wait()
    48  	if data != 250 {
    49  		t.Error("Locking did not safely protect the data")
    50  	}
    51  }
    52  
    53  // TestTryRWMutexBasicTryLock checks that a TryLock will succeed if nobody is
    54  // holding a lock, and will fail if the lock is being held.
    55  func TestTryRWMutexBasicTryLock(t *testing.T) {
    56  	// Lock and then TryLock.
    57  	var tm TryRWMutex
    58  	tm.Lock()
    59  	if tm.TryLock() {
    60  		t.Error("TryLock should have failed")
    61  	}
    62  	tm.Unlock()
    63  
    64  	tm.Lock()
    65  	tm.Unlock()
    66  
    67  	// TryLock and then TryLock.
    68  	if !tm.TryLock() {
    69  		t.Error("Could not get a blank lock")
    70  	}
    71  	if tm.TryLock() {
    72  		t.Error("should not have been able to get the lock")
    73  	}
    74  	tm.Unlock()
    75  }
    76  
    77  // TestTryRWMutexConcurrentTries attempts to grab locks from many threads,
    78  // giving the race detector a chance to detect any issues.
    79  func TestTryRWMutexConncurrentTries(t *testing.T) {
    80  	if testing.Short() {
    81  		t.SkipNow()
    82  	}
    83  
    84  	// Try executing multiple additions concurrently.
    85  	var tm TryRWMutex
    86  	var data int
    87  	var wg sync.WaitGroup
    88  	for i := 0; i < 250; i++ {
    89  		wg.Add(1)
    90  		go func() {
    91  			for !tm.TryLock() {
    92  			}
    93  
    94  			data++
    95  			tm.Unlock()
    96  			wg.Done()
    97  		}()
    98  	}
    99  	wg.Wait()
   100  	if data != 250 {
   101  		t.Error("Locking did not safely protect the data")
   102  	}
   103  }
   104  
   105  // TestTryRWMutexReadAvailable will try to acquire a read lock on the mutex
   106  // when it is supposed to be available.
   107  func TestTryRWMutexReadAvailable(t *testing.T) {
   108  	var tm TryRWMutex
   109  	if !tm.TryRLock() {
   110  		t.Fatal("Unable to get readlock on a fresh TryRWMutex")
   111  	}
   112  
   113  	// Grab the lock and increment the data in a goroutine.
   114  	var data int
   115  	var wg sync.WaitGroup
   116  	wg.Add(2)
   117  	go func() {
   118  		defer wg.Done()
   119  		tm.Lock()
   120  		data++
   121  		tm.Unlock()
   122  	}()
   123  	runtime.Gosched()
   124  	go func() {
   125  		defer wg.Done()
   126  		tm.Lock()
   127  		data++
   128  		tm.Unlock()
   129  	}()
   130  	runtime.Gosched()
   131  
   132  	// Read the data, readlock should be held.
   133  	if data != 0 {
   134  		t.Fatal("Data should not have changed while under readlock")
   135  	}
   136  
   137  	// Release the lock and wait for the other locks to finish their
   138  	// modifications.
   139  	tm.RUnlock()
   140  	wg.Wait()
   141  
   142  	// Try to grab another readlock. It should succeed. The data should have
   143  	// changed.
   144  	if !tm.TryRLock() {
   145  		t.Fatal("Unable to get readlock on available TryRWMutex")
   146  	}
   147  	if data != 2 {
   148  		t.Error("Data does not seem to have been altered correctly")
   149  	}
   150  	tm.RUnlock()
   151  }
   152  
   153  // TestTryRWMutexReadUnavailable will try to acquire a read lock on the mutex
   154  // when it is supposed to be available.
   155  func TestTryRWMutexReadUnavailable(t *testing.T) {
   156  	var tm TryRWMutex
   157  	if !tm.TryRLock() {
   158  		t.Fatal("Unable to get readlock on a fresh TryRWMutex")
   159  	}
   160  
   161  	// Grab the lock and increment the data in a goroutine.
   162  	var data int
   163  	var wg sync.WaitGroup
   164  	wg.Add(2)
   165  	go func() {
   166  		defer wg.Done()
   167  		tm.Lock()
   168  		data++
   169  		tm.Unlock()
   170  	}()
   171  	runtime.Gosched()
   172  	go func() {
   173  		defer wg.Done()
   174  		tm.Lock()
   175  		data++
   176  		tm.Unlock()
   177  	}()
   178  	runtime.Gosched()
   179  
   180  	// Read the data, readlock should be held.
   181  	if data != 0 {
   182  		t.Fatal("Data should not have changed while under readlock")
   183  	}
   184  
   185  	// Try to grab another readlock. It should not succeed.
   186  	if tm.TryRLock() {
   187  		t.Fatal("Able to get readlock on available TryRWMutex")
   188  	}
   189  
   190  	// Release the lock and wait for the other locks to finish their
   191  	// modifications.
   192  	tm.RUnlock()
   193  	wg.Wait()
   194  }