github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/sync/lock_test.go (about)

     1  package sync
     2  
     3  import (
     4  	"runtime"
     5  	"sync"
     6  	"testing"
     7  	"time"
     8  )
     9  
    10  // TestLowThreadLocking checks that locks are functional in the safelock
    11  // mechanism, only 2 threads are used to try and trigger a race condition.
    12  func TestLowThreadLocking(t *testing.T) {
    13  	if testing.Short() {
    14  		t.SkipNow()
    15  	}
    16  
    17  	// Create a value and lock a mutex to protect the value.
    18  	value := 0
    19  	safeLock := New(time.Second, 1)
    20  	outerID := safeLock.Lock()
    21  	go func() {
    22  		// Lock a mutex and read the value. Value should be 1, since the old
    23  		// mutex was not released
    24  		innerID := safeLock.Lock()
    25  		defer safeLock.Unlock(innerID)
    26  		if value != 1 {
    27  			t.Fatal("Lock was grabbed incorrectly")
    28  		}
    29  	}()
    30  
    31  	// After spawning the other thread, increment value.
    32  	value = 1
    33  	safeLock.Unlock(outerID)
    34  }
    35  
    36  // TestHighThreadLocking tries to trigger race conditions while using lots of
    37  // threads and sleep tactics.
    38  func TestHighThreadLocking(t *testing.T) {
    39  	if testing.Short() {
    40  		t.SkipNow()
    41  	}
    42  
    43  	// Try to trigger a race condition by using lots of threads.
    44  	for i := 0; i < 50; i++ {
    45  		go func() {
    46  			// Create a value and lock a mutex to protect the value.
    47  			value := 0
    48  			safeLock := New(time.Second, 1)
    49  			outerID := safeLock.Lock()
    50  			go func() {
    51  				// Lock a mutex and read the value. Value should be 1, since
    52  				// the old mutex was not released
    53  				innerID := safeLock.Lock()
    54  				defer safeLock.Unlock(innerID)
    55  				if value != 1 {
    56  					t.Fatal("Lock was grabbed incorrectly")
    57  				}
    58  			}()
    59  
    60  			// Some sleeps and a call to gosched to try and give the thread
    61  			// control to the spawned thread.
    62  			time.Sleep(time.Millisecond * 25)
    63  			runtime.Gosched()
    64  			time.Sleep(time.Millisecond * 25)
    65  			value = 1
    66  			safeLock.Unlock(outerID)
    67  		}()
    68  	}
    69  }
    70  
    71  // TestReadLocking checks that the readlocks can overlap without interference
    72  // from a writelock.
    73  func TestReadLocking(t *testing.T) {
    74  	if testing.Short() {
    75  		t.SkipNow()
    76  	}
    77  
    78  	startTime := time.Now().Unix()
    79  	value := 0
    80  	safeLock := New(time.Second, 1)
    81  	writeID := safeLock.Lock()
    82  
    83  	readThreads := 100
    84  	var wg sync.WaitGroup
    85  	wg.Add(readThreads)
    86  	for i := 0; i < readThreads; i++ {
    87  		go func() {
    88  			readID := safeLock.RLock()
    89  			defer safeLock.RUnlock(readID)
    90  
    91  			if value != 1 {
    92  				t.Error("reading is not happening correctly")
    93  			}
    94  
    95  			// Sleep 250 milliseconds after grabbing the readlock. Because
    96  			// there are a bunch of threads, if the readlocks are not grabbing
    97  			// the lock in parallel the test will take a long time.
    98  			time.Sleep(time.Millisecond * 250)
    99  			wg.Done()
   100  		}()
   101  	}
   102  	value = 1
   103  
   104  	// A combination of sleep and gosched to give priority to the other
   105  	// threads.
   106  	time.Sleep(time.Millisecond * 100)
   107  	runtime.Gosched()
   108  	time.Sleep(time.Millisecond * 100)
   109  	safeLock.Unlock(writeID)
   110  
   111  	// Wait for all of the threads to finish sleeping.
   112  	wg.Wait()
   113  	// Check that the whole test took under 3 seconds. If the readlocks were
   114  	// efficiently being grabbed in parallel, the test should be subtantially
   115  	// less than 3 seconds.
   116  	if time.Now().Unix()-startTime > 3 {
   117  		t.Error("test took too long to complete")
   118  	}
   119  }
   120  
   121  // TestLockSafety checks that a safelock correctly unwinds a deadlock.
   122  func TestLockSafety(t *testing.T) {
   123  	if testing.Short() {
   124  		t.SkipNow()
   125  	}
   126  
   127  	startTime := time.Now().Unix()
   128  	safeLock := New(time.Millisecond*25, 1)
   129  
   130  	// Trigger a deadlock by writelocking twice. The deadlock detector should
   131  	// resolve the issue.
   132  	outerWrite := safeLock.Lock()
   133  	innerWrite := safeLock.Lock()
   134  	safeLock.Unlock(outerWrite)
   135  	safeLock.Unlock(innerWrite)
   136  
   137  	// Trigger a deadlock by readlocking and then writelocking. The deadlock
   138  	// detector should resolve the issue.
   139  	readID := safeLock.RLock()
   140  	writeID := safeLock.Lock()
   141  	safeLock.RUnlock(readID)
   142  	safeLock.Unlock(writeID)
   143  
   144  	// Check that the whole test took under 3 seconds. If the deadlock detector
   145  	// is working, the time elapsed should be much less than 3 seconds.
   146  	if time.Now().Unix()-startTime > 2 {
   147  		t.Error("test took too long to complete")
   148  	}
   149  }