1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
|
package sync
import (
"sync"
"testing"
"time"
)
// TestTryMutexBasicMutex verifies that Lock and Unlock work the same as a
// normal mutex would.
func TestTryMutexBasicMutex(t *testing.T) {
// Check that two calls to lock will execute in the correct order.
var tm TryMutex
var data int
tm.Lock()
go func() {
data = 15
tm.Unlock()
}()
tm.Lock()
if data != 15 {
t.Error("Locking did not safely protect the data")
}
tm.Unlock()
}
// TestTryMutexConcurrentLocking checks that doing lots of concurrent locks is
// handled as expected.
func TestTryMutexConcurrentLocking(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
// Try executing multiple additions concurrently.
var tm TryMutex
var data int
var wg sync.WaitGroup
for i := 0; i < 250; i++ {
wg.Add(1)
go func() {
tm.Lock()
data++
tm.Unlock()
wg.Done()
}()
}
wg.Wait()
if data != 250 {
t.Error("Locking did not safely protect the data")
}
}
// TestTryMutexBasicTryLock checks that a TryLock will succeed if nobody is
// holding a lock, and will fail if the lock is being held.
func TestTryMutexBasicTryLock(t *testing.T) {
// Lock and then TryLock.
var tm TryMutex
tm.Lock()
if tm.TryLock() {
t.Error("TryLock should have failed")
}
tm.Unlock()
tm.Lock()
tm.Unlock()
// TryLock and then TryLock.
if !tm.TryLock() {
t.Error("Could not get a blank lock")
}
if tm.TryLock() {
t.Error("should not have been able to get the lock")
}
tm.Unlock()
}
// TestTryMutexConcurrentTries attempts to grab locks from many threads, giving
// the race detector a chance to detect any issues.
func TestTryMutexConncurrentTries(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
// Try executing multiple additions concurrently.
var tm TryMutex
var data int
var wg sync.WaitGroup
for i := 0; i < 250; i++ {
wg.Add(1)
go func() {
for !tm.TryLock() {
}
data++
tm.Unlock()
wg.Done()
}()
}
wg.Wait()
if data != 250 {
t.Error("Locking did not safely protect the data")
}
}
// TestTryMutexTimed checks that a timed lock will correctly time out if it
// cannot grab a lock.
func TestTryMutexTimed(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
var tm TryMutex
tm.Lock()
startTime := time.Now()
if tm.TryLockTimed(time.Millisecond * 500) {
t.Error("was able to grab a locked lock")
}
wait := time.Now().Sub(startTime)
if wait < time.Millisecond*450 {
t.Error("lock did not wait the correct amount of time before timing out", wait)
}
if wait > time.Millisecond*900 {
t.Error("lock waited too long before timing out", wait)
}
tm.Unlock()
if !tm.TryLockTimed(time.Millisecond * 1) {
t.Error("Unable to get an unlocked lock")
}
tm.Unlock()
}
// TestTryMutexTimedConcurrent checks that a timed lock will correctly time out
// if it cannot grab a lock.
func TestTryMutexTimedConcurrent(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
var tm TryMutex
// Engage a lock and launch a gothread to wait for a lock, fail, and then
// call unlock.
tm.Lock()
go func() {
startTime := time.Now()
if tm.TryLockTimed(time.Millisecond * 500) {
t.Error("was able to grab a locked lock")
}
wait := time.Now().Sub(startTime)
if wait < time.Millisecond*450 {
t.Error("lock did not wait the correct amount of time before timing out:", wait)
}
if wait > time.Millisecond*900 {
t.Error("lock waited too long before timing out", wait)
}
tm.Unlock()
}()
// Try to get a lock, but don't wait long enough.
if tm.TryLockTimed(time.Millisecond * 250) {
// Lock shoud time out because the gothread responsible for releasing
// the lock will be idle for 500 milliseconds.
t.Error("Lock should have timed out")
}
if !tm.TryLockTimed(time.Millisecond * 950) {
// Lock should be successful - the above thread should finish in under
// 950 milliseconds.
t.Error("Lock should have been successful")
}
tm.Unlock()
}
|