github.com/NebulousLabs/Sia@v1.3.7/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 }