github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/lsync/lrwmutex_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package lsync_test 19 20 import ( 21 "context" 22 "fmt" 23 "runtime" 24 "sync" 25 "sync/atomic" 26 "testing" 27 "time" 28 29 . "github.com/minio/minio/internal/lsync" 30 ) 31 32 func testSimpleWriteLock(t *testing.T, duration time.Duration) (locked bool) { 33 ctx := context.Background() 34 lrwm := NewLRWMutex() 35 36 if !lrwm.GetRLock(ctx, "", "object1", time.Second) { 37 panic("Failed to acquire read lock") 38 } 39 // fmt.Println("1st read lock acquired, waiting...") 40 41 if !lrwm.GetRLock(ctx, "", "object1", time.Second) { 42 panic("Failed to acquire read lock") 43 } 44 // fmt.Println("2nd read lock acquired, waiting...") 45 46 go func() { 47 time.Sleep(2 * time.Second) 48 lrwm.RUnlock() 49 // fmt.Println("1st read lock released, waiting...") 50 }() 51 52 go func() { 53 time.Sleep(3 * time.Second) 54 lrwm.RUnlock() 55 // fmt.Println("2nd read lock released, waiting...") 56 }() 57 58 // fmt.Println("Trying to acquire write lock, waiting...") 59 locked = lrwm.GetLock(ctx, "", "", duration) 60 if locked { 61 // fmt.Println("Write lock acquired, waiting...") 62 time.Sleep(1 * time.Second) 63 64 lrwm.Unlock() 65 } else { 66 t.Log("Write lock failed due to timeout") 67 } 68 return 69 } 70 71 func TestSimpleWriteLockAcquired(t *testing.T) { 72 locked := testSimpleWriteLock(t, 5*time.Second) 73 74 expected := true 75 if locked != expected { 76 t.Errorf("TestSimpleWriteLockAcquired(): \nexpected %#v\ngot %#v", expected, locked) 77 } 78 } 79 80 func TestSimpleWriteLockTimedOut(t *testing.T) { 81 locked := testSimpleWriteLock(t, time.Second) 82 83 expected := false 84 if locked != expected { 85 t.Errorf("TestSimpleWriteLockTimedOut(): \nexpected %#v\ngot %#v", expected, locked) 86 } 87 } 88 89 func testDualWriteLock(t *testing.T, duration time.Duration) (locked bool) { 90 ctx := context.Background() 91 lrwm := NewLRWMutex() 92 93 // fmt.Println("Getting initial write lock") 94 if !lrwm.GetLock(ctx, "", "", time.Second) { 95 panic("Failed to acquire initial write lock") 96 } 97 98 go func() { 99 time.Sleep(2 * time.Second) 100 lrwm.Unlock() 101 // fmt.Println("Initial write lock released, waiting...") 102 }() 103 104 // fmt.Println("Trying to acquire 2nd write lock, waiting...") 105 locked = lrwm.GetLock(ctx, "", "", duration) 106 if locked { 107 // fmt.Println("2nd write lock acquired, waiting...") 108 time.Sleep(time.Second) 109 110 lrwm.Unlock() 111 } else { 112 t.Log("2nd write lock failed due to timeout") 113 } 114 return 115 } 116 117 func TestDualWriteLockAcquired(t *testing.T) { 118 locked := testDualWriteLock(t, 3*time.Second) 119 120 expected := true 121 if locked != expected { 122 t.Errorf("TestDualWriteLockAcquired(): \nexpected %#v\ngot %#v", expected, locked) 123 } 124 } 125 126 func TestDualWriteLockTimedOut(t *testing.T) { 127 locked := testDualWriteLock(t, time.Second) 128 129 expected := false 130 if locked != expected { 131 t.Errorf("TestDualWriteLockTimedOut(): \nexpected %#v\ngot %#v", expected, locked) 132 } 133 } 134 135 // Test cases below are copied 1 to 1 from sync/rwmutex_test.go (adapted to use LRWMutex) 136 137 // Borrowed from rwmutex_test.go 138 func parallelReader(ctx context.Context, m *LRWMutex, clocked, cunlock, cdone chan bool) { 139 if m.GetRLock(ctx, "", "", time.Second) { 140 clocked <- true 141 <-cunlock 142 m.RUnlock() 143 cdone <- true 144 } 145 } 146 147 // Borrowed from rwmutex_test.go 148 func doTestParallelReaders(numReaders, gomaxprocs int) { 149 runtime.GOMAXPROCS(gomaxprocs) 150 m := NewLRWMutex() 151 152 clocked := make(chan bool) 153 cunlock := make(chan bool) 154 cdone := make(chan bool) 155 for i := 0; i < numReaders; i++ { 156 go parallelReader(context.Background(), m, clocked, cunlock, cdone) 157 } 158 // Wait for all parallel RLock()s to succeed. 159 for i := 0; i < numReaders; i++ { 160 <-clocked 161 } 162 for i := 0; i < numReaders; i++ { 163 cunlock <- true 164 } 165 // Wait for the goroutines to finish. 166 for i := 0; i < numReaders; i++ { 167 <-cdone 168 } 169 } 170 171 // Borrowed from rwmutex_test.go 172 func TestParallelReaders(t *testing.T) { 173 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) 174 doTestParallelReaders(1, 4) 175 doTestParallelReaders(3, 4) 176 doTestParallelReaders(4, 2) 177 } 178 179 // Borrowed from rwmutex_test.go 180 func reader(rwm *LRWMutex, numIterations int, activity *int32, cdone chan bool) { 181 for i := 0; i < numIterations; i++ { 182 if rwm.GetRLock(context.Background(), "", "", time.Second) { 183 n := atomic.AddInt32(activity, 1) 184 if n < 1 || n >= 10000 { 185 panic(fmt.Sprintf("wlock(%d)\n", n)) 186 } 187 for i := 0; i < 100; i++ { 188 } 189 atomic.AddInt32(activity, -1) 190 rwm.RUnlock() 191 } 192 } 193 cdone <- true 194 } 195 196 // Borrowed from rwmutex_test.go 197 func writer(rwm *LRWMutex, numIterations int, activity *int32, cdone chan bool) { 198 for i := 0; i < numIterations; i++ { 199 if rwm.GetLock(context.Background(), "", "", time.Second) { 200 n := atomic.AddInt32(activity, 10000) 201 if n != 10000 { 202 panic(fmt.Sprintf("wlock(%d)\n", n)) 203 } 204 for i := 0; i < 100; i++ { 205 } 206 atomic.AddInt32(activity, -10000) 207 rwm.Unlock() 208 } 209 } 210 cdone <- true 211 } 212 213 // Borrowed from rwmutex_test.go 214 func HammerRWMutex(gomaxprocs, numReaders, numIterations int) { 215 runtime.GOMAXPROCS(gomaxprocs) 216 // Number of active readers + 10000 * number of active writers. 217 var activity int32 218 rwm := NewLRWMutex() 219 cdone := make(chan bool) 220 go writer(rwm, numIterations, &activity, cdone) 221 var i int 222 for i = 0; i < numReaders/2; i++ { 223 go reader(rwm, numIterations, &activity, cdone) 224 } 225 go writer(rwm, numIterations, &activity, cdone) 226 for ; i < numReaders; i++ { 227 go reader(rwm, numIterations, &activity, cdone) 228 } 229 // Wait for the 2 writers and all readers to finish. 230 for i := 0; i < 2+numReaders; i++ { 231 <-cdone 232 } 233 } 234 235 // Borrowed from rwmutex_test.go 236 func TestRWMutex(t *testing.T) { 237 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) 238 n := 1000 239 if testing.Short() { 240 n = 5 241 } 242 HammerRWMutex(1, 1, n) 243 HammerRWMutex(1, 3, n) 244 HammerRWMutex(1, 10, n) 245 HammerRWMutex(4, 1, n) 246 HammerRWMutex(4, 3, n) 247 HammerRWMutex(4, 10, n) 248 HammerRWMutex(10, 1, n) 249 HammerRWMutex(10, 3, n) 250 HammerRWMutex(10, 10, n) 251 HammerRWMutex(10, 5, n) 252 } 253 254 // Borrowed from rwmutex_test.go 255 func TestDRLocker(t *testing.T) { 256 wl := NewLRWMutex() 257 var rl sync.Locker 258 wlocked := make(chan bool, 1) 259 rlocked := make(chan bool, 1) 260 rl = wl.DRLocker() 261 n := 10 262 go func() { 263 for i := 0; i < n; i++ { 264 rl.Lock() 265 rl.Lock() 266 rlocked <- true 267 wl.Lock() 268 wlocked <- true 269 } 270 }() 271 for i := 0; i < n; i++ { 272 <-rlocked 273 rl.Unlock() 274 select { 275 case <-wlocked: 276 t.Fatal("RLocker() didn't read-lock it") 277 default: 278 } 279 rl.Unlock() 280 <-wlocked 281 select { 282 case <-rlocked: 283 t.Fatal("RLocker() didn't respect the write lock") 284 default: 285 } 286 wl.Unlock() 287 } 288 } 289 290 // Borrowed from rwmutex_test.go 291 func TestUnlockPanic(t *testing.T) { 292 defer func() { 293 if recover() == nil { 294 t.Fatalf("unlock of unlocked RWMutex did not panic") 295 } 296 }() 297 mu := NewLRWMutex() 298 mu.Unlock() 299 } 300 301 // Borrowed from rwmutex_test.go 302 func TestUnlockPanic2(t *testing.T) { 303 defer func() { 304 if recover() == nil { 305 t.Fatalf("unlock of unlocked RWMutex did not panic") 306 } 307 }() 308 mu := NewLRWMutex() 309 mu.RLock() 310 mu.Unlock() 311 } 312 313 // Borrowed from rwmutex_test.go 314 func TestRUnlockPanic(t *testing.T) { 315 defer func() { 316 if recover() == nil { 317 t.Fatalf("read unlock of unlocked RWMutex did not panic") 318 } 319 }() 320 mu := NewLRWMutex() 321 mu.RUnlock() 322 } 323 324 // Borrowed from rwmutex_test.go 325 func TestRUnlockPanic2(t *testing.T) { 326 defer func() { 327 if recover() == nil { 328 t.Fatalf("read unlock of unlocked RWMutex did not panic") 329 } 330 }() 331 mu := NewLRWMutex() 332 mu.Lock() 333 mu.RUnlock() 334 }