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