storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/dsync/drwmutex_test.go (about) 1 /* 2 * Minio Cloud Storage, (C) 2016 Minio, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dsync_test 18 19 import ( 20 "context" 21 "fmt" 22 "runtime" 23 "sync/atomic" 24 "testing" 25 "time" 26 27 . "storj.io/minio/pkg/dsync" 28 ) 29 30 const ( 31 id = "1234-5678" 32 source = "main.go" 33 ) 34 35 func testSimpleWriteLock(t *testing.T, duration time.Duration) (locked bool) { 36 37 drwm := NewDRWMutex(ds, "simplelock") 38 39 ctx1, cancel1 := context.WithCancel(context.Background()) 40 if !drwm.GetRLock(ctx1, cancel1, id, source, Options{Timeout: time.Second}) { 41 panic("Failed to acquire read lock") 42 } 43 // fmt.Println("1st read lock acquired, waiting...") 44 45 ctx2, cancel2 := context.WithCancel(context.Background()) 46 if !drwm.GetRLock(ctx2, cancel2, id, source, Options{Timeout: time.Second}) { 47 panic("Failed to acquire read lock") 48 } 49 // fmt.Println("2nd read lock acquired, waiting...") 50 51 go func() { 52 time.Sleep(2 * time.Second) 53 drwm.RUnlock() 54 // fmt.Println("1st read lock released, waiting...") 55 }() 56 57 go func() { 58 time.Sleep(3 * time.Second) 59 drwm.RUnlock() 60 // fmt.Println("2nd read lock released, waiting...") 61 }() 62 63 // fmt.Println("Trying to acquire write lock, waiting...") 64 ctx3, cancel3 := context.WithCancel(context.Background()) 65 locked = drwm.GetLock(ctx3, cancel3, id, source, Options{Timeout: duration}) 66 if locked { 67 // fmt.Println("Write lock acquired, waiting...") 68 time.Sleep(time.Second) 69 70 drwm.Unlock() 71 } 72 // fmt.Println("Write lock failed due to timeout") 73 return 74 } 75 76 func TestSimpleWriteLockAcquired(t *testing.T) { 77 locked := testSimpleWriteLock(t, 5*time.Second) 78 79 expected := true 80 if locked != expected { 81 t.Errorf("TestSimpleWriteLockAcquired(): \nexpected %#v\ngot %#v", expected, locked) 82 } 83 } 84 85 func TestSimpleWriteLockTimedOut(t *testing.T) { 86 locked := testSimpleWriteLock(t, time.Second) 87 88 expected := false 89 if locked != expected { 90 t.Errorf("TestSimpleWriteLockTimedOut(): \nexpected %#v\ngot %#v", expected, locked) 91 } 92 } 93 94 func testDualWriteLock(t *testing.T, duration time.Duration) (locked bool) { 95 96 drwm := NewDRWMutex(ds, "duallock") 97 98 // fmt.Println("Getting initial write lock") 99 ctx1, cancel1 := context.WithCancel(context.Background()) 100 if !drwm.GetLock(ctx1, cancel1, id, source, Options{Timeout: time.Second}) { 101 panic("Failed to acquire initial write lock") 102 } 103 104 go func() { 105 time.Sleep(2 * time.Second) 106 drwm.Unlock() 107 // fmt.Println("Initial write lock released, waiting...") 108 }() 109 110 // fmt.Println("Trying to acquire 2nd write lock, waiting...") 111 ctx2, cancel2 := context.WithCancel(context.Background()) 112 locked = drwm.GetLock(ctx2, cancel2, id, source, Options{Timeout: duration}) 113 if locked { 114 // fmt.Println("2nd write lock acquired, waiting...") 115 time.Sleep(time.Second) 116 117 drwm.Unlock() 118 } 119 // fmt.Println("2nd write lock failed due to timeout") 120 return 121 } 122 123 func TestDualWriteLockAcquired(t *testing.T) { 124 locked := testDualWriteLock(t, 5*time.Second) 125 126 expected := true 127 if locked != expected { 128 t.Errorf("TestDualWriteLockAcquired(): \nexpected %#v\ngot %#v", expected, locked) 129 } 130 131 } 132 133 func TestDualWriteLockTimedOut(t *testing.T) { 134 locked := testDualWriteLock(t, time.Second) 135 136 expected := false 137 if locked != expected { 138 t.Errorf("TestDualWriteLockTimedOut(): \nexpected %#v\ngot %#v", expected, locked) 139 } 140 141 } 142 143 // Test cases below are copied 1 to 1 from sync/rwmutex_test.go (adapted to use DRWMutex) 144 145 // Borrowed from rwmutex_test.go 146 func parallelReader(ctx context.Context, m *DRWMutex, clocked, cunlock, cdone chan bool) { 147 if m.GetRLock(ctx, nil, id, source, Options{Timeout: time.Second}) { 148 clocked <- true 149 <-cunlock 150 m.RUnlock() 151 cdone <- true 152 } 153 } 154 155 // Borrowed from rwmutex_test.go 156 func doTestParallelReaders(numReaders, gomaxprocs int) { 157 runtime.GOMAXPROCS(gomaxprocs) 158 m := NewDRWMutex(ds, "test-parallel") 159 160 clocked := make(chan bool) 161 cunlock := make(chan bool) 162 cdone := make(chan bool) 163 for i := 0; i < numReaders; i++ { 164 go parallelReader(context.Background(), m, clocked, cunlock, cdone) 165 } 166 // Wait for all parallel RLock()s to succeed. 167 for i := 0; i < numReaders; i++ { 168 <-clocked 169 } 170 for i := 0; i < numReaders; i++ { 171 cunlock <- true 172 } 173 // Wait for the goroutines to finish. 174 for i := 0; i < numReaders; i++ { 175 <-cdone 176 } 177 } 178 179 // Borrowed from rwmutex_test.go 180 func TestParallelReaders(t *testing.T) { 181 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) 182 doTestParallelReaders(1, 4) 183 doTestParallelReaders(3, 4) 184 doTestParallelReaders(4, 2) 185 } 186 187 // Borrowed from rwmutex_test.go 188 func reader(rwm *DRWMutex, numIterations int, activity *int32, cdone chan bool) { 189 for i := 0; i < numIterations; i++ { 190 if rwm.GetRLock(context.Background(), nil, id, source, Options{Timeout: time.Second}) { 191 n := atomic.AddInt32(activity, 1) 192 if n < 1 || n >= 10000 { 193 panic(fmt.Sprintf("wlock(%d)\n", n)) 194 } 195 for i := 0; i < 100; i++ { 196 } 197 atomic.AddInt32(activity, -1) 198 rwm.RUnlock() 199 } 200 } 201 cdone <- true 202 } 203 204 // Borrowed from rwmutex_test.go 205 func writer(rwm *DRWMutex, numIterations int, activity *int32, cdone chan bool) { 206 for i := 0; i < numIterations; i++ { 207 if rwm.GetLock(context.Background(), nil, id, source, Options{Timeout: time.Second}) { 208 n := atomic.AddInt32(activity, 10000) 209 if n != 10000 { 210 panic(fmt.Sprintf("wlock(%d)\n", n)) 211 } 212 for i := 0; i < 100; i++ { 213 } 214 atomic.AddInt32(activity, -10000) 215 rwm.Unlock() 216 } 217 } 218 cdone <- true 219 } 220 221 // Borrowed from rwmutex_test.go 222 func HammerRWMutex(gomaxprocs, numReaders, numIterations int) { 223 runtime.GOMAXPROCS(gomaxprocs) 224 // Number of active readers + 10000 * number of active writers. 225 var activity int32 226 rwm := NewDRWMutex(ds, "test") 227 cdone := make(chan bool) 228 go writer(rwm, numIterations, &activity, cdone) 229 var i int 230 for i = 0; i < numReaders/2; i++ { 231 go reader(rwm, numIterations, &activity, cdone) 232 } 233 go writer(rwm, numIterations, &activity, cdone) 234 for ; i < numReaders; i++ { 235 go reader(rwm, numIterations, &activity, cdone) 236 } 237 // Wait for the 2 writers and all readers to finish. 238 for i := 0; i < 2+numReaders; i++ { 239 <-cdone 240 } 241 } 242 243 // Borrowed from rwmutex_test.go 244 func TestRWMutex(t *testing.T) { 245 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) 246 n := 100 247 if testing.Short() { 248 n = 5 249 } 250 HammerRWMutex(1, 1, n) 251 HammerRWMutex(1, 3, n) 252 HammerRWMutex(1, 10, n) 253 HammerRWMutex(4, 1, n) 254 HammerRWMutex(4, 3, n) 255 HammerRWMutex(4, 10, n) 256 HammerRWMutex(10, 1, n) 257 HammerRWMutex(10, 3, n) 258 HammerRWMutex(10, 10, n) 259 HammerRWMutex(10, 5, n) 260 } 261 262 // Borrowed from rwmutex_test.go 263 func TestUnlockPanic(t *testing.T) { 264 defer func() { 265 if recover() == nil { 266 t.Fatalf("unlock of unlocked RWMutex did not panic") 267 } 268 }() 269 mu := NewDRWMutex(ds, "test") 270 mu.Unlock() 271 } 272 273 // Borrowed from rwmutex_test.go 274 func TestUnlockPanic2(t *testing.T) { 275 defer func() { 276 if recover() == nil { 277 t.Fatalf("unlock of unlocked RWMutex did not panic") 278 } 279 }() 280 mu := NewDRWMutex(ds, "test-unlock-panic-2") 281 mu.RLock(id, source) 282 mu.Unlock() 283 } 284 285 // Borrowed from rwmutex_test.go 286 func TestRUnlockPanic(t *testing.T) { 287 defer func() { 288 if recover() == nil { 289 t.Fatalf("read unlock of unlocked RWMutex did not panic") 290 } 291 }() 292 mu := NewDRWMutex(ds, "test") 293 mu.RUnlock() 294 } 295 296 // Borrowed from rwmutex_test.go 297 func TestRUnlockPanic2(t *testing.T) { 298 defer func() { 299 if recover() == nil { 300 t.Fatalf("read unlock of unlocked RWMutex did not panic") 301 } 302 }() 303 mu := NewDRWMutex(ds, "test-runlock-panic-2") 304 mu.Lock(id, source) 305 mu.RUnlock() 306 } 307 308 // Borrowed from rwmutex_test.go 309 func benchmarkRWMutex(b *testing.B, localWork, writeRatio int) { 310 rwm := NewDRWMutex(ds, "test") 311 b.RunParallel(func(pb *testing.PB) { 312 foo := 0 313 for pb.Next() { 314 foo++ 315 if foo%writeRatio == 0 { 316 rwm.Lock(id, source) 317 rwm.Unlock() 318 } else { 319 rwm.RLock(id, source) 320 for i := 0; i != localWork; i++ { 321 foo *= 2 322 foo /= 2 323 } 324 rwm.RUnlock() 325 } 326 } 327 _ = foo 328 }) 329 } 330 331 // Borrowed from rwmutex_test.go 332 func BenchmarkRWMutexWrite100(b *testing.B) { 333 benchmarkRWMutex(b, 0, 100) 334 } 335 336 // Borrowed from rwmutex_test.go 337 func BenchmarkRWMutexWrite10(b *testing.B) { 338 benchmarkRWMutex(b, 0, 10) 339 } 340 341 // Borrowed from rwmutex_test.go 342 func BenchmarkRWMutexWorkWrite100(b *testing.B) { 343 benchmarkRWMutex(b, 100, 100) 344 } 345 346 // Borrowed from rwmutex_test.go 347 func BenchmarkRWMutexWorkWrite10(b *testing.B) { 348 benchmarkRWMutex(b, 100, 10) 349 }