github.com/nebulouslabs/sia@v1.3.7/sync/trymutex_test.go (about)

     1  package sync
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  )
     8  
     9  // TestTryMutexBasicMutex verifies that Lock and Unlock work the same as a
    10  // normal mutex would.
    11  func TestTryMutexBasicMutex(t *testing.T) {
    12  	// Check that two calls to lock will execute in the correct order.
    13  	var tm TryMutex
    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  // TestTryMutexConcurrentLocking checks that doing lots of concurrent locks is
    28  // handled as expected.
    29  func TestTryMutexConcurrentLocking(t *testing.T) {
    30  	if testing.Short() {
    31  		t.SkipNow()
    32  	}
    33  
    34  	// Try executing multiple additions concurrently.
    35  	var tm TryMutex
    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  // TestTryMutexBasicTryLock checks that a TryLock will succeed if nobody is
    54  // holding a lock, and will fail if the lock is being held.
    55  func TestTryMutexBasicTryLock(t *testing.T) {
    56  	// Lock and then TryLock.
    57  	var tm TryMutex
    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  // TestTryMutexConcurrentTries attempts to grab locks from many threads, giving
    78  // the race detector a chance to detect any issues.
    79  func TestTryMutexConncurrentTries(t *testing.T) {
    80  	if testing.Short() {
    81  		t.SkipNow()
    82  	}
    83  
    84  	// Try executing multiple additions concurrently.
    85  	var tm TryMutex
    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  // TestTryMutexTimed checks that a timed lock will correctly time out if it
   106  // cannot grab a lock.
   107  func TestTryMutexTimed(t *testing.T) {
   108  	if testing.Short() {
   109  		t.SkipNow()
   110  	}
   111  
   112  	var tm TryMutex
   113  	tm.Lock()
   114  
   115  	startTime := time.Now()
   116  	if tm.TryLockTimed(time.Millisecond * 500) {
   117  		t.Error("was able to grab a locked lock")
   118  	}
   119  
   120  	wait := time.Now().Sub(startTime)
   121  	if wait < time.Millisecond*450 {
   122  		t.Error("lock did not wait the correct amount of time before timing out", wait)
   123  	}
   124  	if wait > time.Millisecond*900 {
   125  		t.Error("lock waited too long before timing out", wait)
   126  	}
   127  
   128  	tm.Unlock()
   129  	if !tm.TryLockTimed(time.Millisecond * 1) {
   130  		t.Error("Unable to get an unlocked lock")
   131  	}
   132  	tm.Unlock()
   133  }
   134  
   135  // TestTryMutexTimedConcurrent checks that a timed lock will correctly time out
   136  // if it cannot grab a lock.
   137  func TestTryMutexTimedConcurrent(t *testing.T) {
   138  	if testing.Short() {
   139  		t.SkipNow()
   140  	}
   141  
   142  	var tm TryMutex
   143  
   144  	// Engage a lock and launch a gothread to wait for a lock, fail, and then
   145  	// call unlock.
   146  	tm.Lock()
   147  	go func() {
   148  		startTime := time.Now()
   149  		if tm.TryLockTimed(time.Millisecond * 500) {
   150  			t.Error("was able to grab a locked lock")
   151  		}
   152  
   153  		wait := time.Now().Sub(startTime)
   154  		if wait < time.Millisecond*450 {
   155  			t.Error("lock did not wait the correct amount of time before timing out:", wait)
   156  		}
   157  		if wait > time.Millisecond*900 {
   158  			t.Error("lock waited too long before timing out", wait)
   159  		}
   160  
   161  		tm.Unlock()
   162  	}()
   163  
   164  	// Try to get a lock, but don't wait long enough.
   165  	if tm.TryLockTimed(time.Millisecond * 250) {
   166  		// Lock shoud time out because the gothread responsible for releasing
   167  		// the lock will be idle for 500 milliseconds.
   168  		t.Error("Lock should have timed out")
   169  	}
   170  	if !tm.TryLockTimed(time.Millisecond * 950) {
   171  		// Lock should be successful - the above thread should finish in under
   172  		// 950 milliseconds.
   173  		t.Error("Lock should have been successful")
   174  	}
   175  	tm.Unlock()
   176  }