github.com/whoyao/protocol@v0.0.0-20230519045905-2d8ace718ca5/utils/lock_tracker_test.go (about) 1 package utils_test 2 3 import ( 4 "fmt" 5 "runtime" 6 "sync" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 12 "github.com/whoyao/protocol/utils" 13 ) 14 15 func init() { 16 utils.EnableLockTracker() 17 } 18 19 func cleanupTest() { 20 runtime.GC() 21 time.Sleep(time.Millisecond) 22 } 23 24 func noop() {} 25 26 func TestScanTrackedLocks(t *testing.T) { 27 t.Cleanup(cleanupTest) 28 assert.Nil(t, utils.ScanTrackedLocks(time.Millisecond)) 29 30 ms := make([]*utils.Mutex, 100) 31 for i := range ms { 32 m := &utils.Mutex{} 33 m.Lock() 34 noop() 35 m.Unlock() 36 ms[i] = m 37 } 38 39 go func() { 40 ms[50].Lock() 41 ms[50].Lock() 42 }() 43 44 time.Sleep(5 * time.Millisecond) 45 assert.NotNil(t, utils.ScanTrackedLocks(time.Millisecond)) 46 47 ms[50].Unlock() 48 } 49 50 func TestMutexFinalizer(t *testing.T) { 51 cleanupTest() 52 assert.Equal(t, 0, utils.NumMutexes()) 53 54 go func() { 55 m := &utils.Mutex{} 56 m.Lock() 57 go func() { 58 m.Unlock() 59 }() 60 assert.Equal(t, 1, utils.NumMutexes()) 61 }() 62 63 time.Sleep(time.Millisecond) 64 cleanupTest() 65 66 assert.Equal(t, 0, utils.NumMutexes()) 67 } 68 69 func TestEmbeddedMutex(t *testing.T) { 70 t.Cleanup(cleanupTest) 71 72 foo := struct{ m utils.Mutex }{} 73 foo.m.Lock() 74 noop() 75 foo.m.Unlock() 76 77 bar := struct{ utils.Mutex }{} 78 bar.Lock() 79 noop() 80 bar.Unlock() 81 } 82 83 func TestContestedGlobalLock(t *testing.T) { 84 t.Cleanup(cleanupTest) 85 86 ms := make([]*utils.Mutex, 100) 87 for i := range ms { 88 m := &utils.Mutex{} 89 m.Lock() 90 noop() 91 m.Unlock() 92 ms[i] = m 93 } 94 95 var wg sync.WaitGroup 96 wg.Add(2) 97 98 go func() { 99 for i := 0; i < 100; i++ { 100 wg.Add(1) 101 go func() { 102 utils.ScanTrackedLocks(time.Minute) 103 wg.Done() 104 }() 105 } 106 wg.Done() 107 }() 108 109 go func() { 110 for i := 0; i < 100; i++ { 111 var m utils.Mutex 112 wg.Add(3) 113 for i := 0; i < 3; i++ { 114 go func() { 115 m.Lock() 116 noop() 117 m.Unlock() 118 wg.Done() 119 }() 120 } 121 } 122 wg.Done() 123 }() 124 125 wg.Wait() 126 } 127 128 func TestInitRace(t *testing.T) { 129 t.Cleanup(cleanupTest) 130 131 var wg sync.WaitGroup 132 for i := 0; i < 100; i++ { 133 var m utils.Mutex 134 wg.Add(3) 135 done := make(chan struct{}) 136 for i := 0; i < 3; i++ { 137 go func() { 138 <-done 139 m.Lock() 140 noop() 141 m.Unlock() 142 wg.Done() 143 }() 144 } 145 close(done) 146 runtime.Gosched() 147 } 148 149 wg.Wait() 150 } 151 152 func BenchmarkLockTracker(b *testing.B) { 153 b.Run("wrapped mutex", func(b *testing.B) { 154 var m utils.Mutex 155 for i := 0; i < b.N; i++ { 156 m.Lock() 157 noop() 158 m.Unlock() 159 } 160 }) 161 b.Run("native mutex", func(b *testing.B) { 162 var m sync.Mutex 163 for i := 0; i < b.N; i++ { 164 m.Lock() 165 noop() 166 m.Unlock() 167 } 168 }) 169 b.Run("wrapped rwmutex", func(b *testing.B) { 170 var m utils.RWMutex 171 for i := 0; i < b.N; i++ { 172 m.Lock() 173 noop() 174 m.Unlock() 175 } 176 }) 177 b.Run("native rwmutex", func(b *testing.B) { 178 var m sync.RWMutex 179 for i := 0; i < b.N; i++ { 180 m.Lock() 181 noop() 182 m.Unlock() 183 } 184 }) 185 186 b.Run("wrapped mutex init", func(b *testing.B) { 187 for i := 0; i < b.N; i++ { 188 var m utils.Mutex 189 m.Lock() 190 noop() 191 m.Unlock() 192 } 193 }) 194 b.Run("native mutex init", func(b *testing.B) { 195 for i := 0; i < b.N; i++ { 196 var m sync.Mutex 197 m.Lock() 198 noop() 199 m.Unlock() 200 } 201 }) 202 b.Run("wrapped rwmutex init", func(b *testing.B) { 203 for i := 0; i < b.N; i++ { 204 var m utils.RWMutex 205 m.Lock() 206 noop() 207 m.Unlock() 208 } 209 }) 210 b.Run("native rwmutex init", func(b *testing.B) { 211 for i := 0; i < b.N; i++ { 212 var m sync.RWMutex 213 m.Lock() 214 noop() 215 m.Unlock() 216 } 217 }) 218 } 219 220 func BenchmarkGetBlocked(b *testing.B) { 221 for n := 100; n <= 1000000; n *= 100 { 222 n := n 223 b.Run(fmt.Sprintf("serial/%d", n), func(b *testing.B) { 224 cleanupTest() 225 226 ms := make([]*utils.Mutex, n) 227 for i := range ms { 228 m := &utils.Mutex{} 229 m.Lock() 230 noop() 231 m.Unlock() 232 ms[i] = m 233 } 234 235 b.ResetTimer() 236 237 for i := 0; i < b.N; i++ { 238 utils.ScanTrackedLocks(time.Minute) 239 } 240 }) 241 } 242 }