storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/namespace-lock_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 cmd 18 19 import ( 20 "context" 21 "runtime" 22 "testing" 23 "time" 24 ) 25 26 // WARNING: 27 // 28 // Expected source line number is hard coded, 31, in the 29 // following test. Adding new code before this test or changing its 30 // position will cause the line number to change and the test to FAIL 31 // Tests getSource(). 32 func TestGetSource(t *testing.T) { 33 currentSource := func() string { return getSource(2) } 34 gotSource := currentSource() 35 // Hard coded line number, 34, in the "expectedSource" value 36 expectedSource := "[namespace-lock_test.go:34:TestGetSource()]" 37 if gotSource != expectedSource { 38 t.Errorf("expected : %s, got : %s", expectedSource, gotSource) 39 } 40 } 41 42 // Test lock race 43 func TestNSLockRace(t *testing.T) { 44 t.Skip("long test skip it") 45 46 ctx := context.Background() 47 48 for i := 0; i < 10000; i++ { 49 nsLk := newNSLock(false) 50 51 // lk1; ref=1 52 if !nsLk.lock(ctx, "volume", "path", "source", "opsID", false, time.Second) { 53 t.Fatal("failed to acquire lock") 54 } 55 56 // lk2 57 lk2ch := make(chan struct{}) 58 go func() { 59 defer close(lk2ch) 60 nsLk.lock(ctx, "volume", "path", "source", "opsID", false, 1*time.Millisecond) 61 }() 62 time.Sleep(1 * time.Millisecond) // wait for goroutine to advance; ref=2 63 64 // Unlock the 1st lock; ref=1 after this line 65 nsLk.unlock("volume", "path", false) 66 67 // Taking another lockMapMutex here allows queuing up additional lockers. This should 68 // not be required but makes reproduction much easier. 69 nsLk.lockMapMutex.Lock() 70 71 // lk3 blocks. 72 lk3ch := make(chan bool) 73 go func() { 74 lk3ch <- nsLk.lock(ctx, "volume", "path", "source", "opsID", false, 0) 75 }() 76 77 // lk4, blocks. 78 lk4ch := make(chan bool) 79 go func() { 80 lk4ch <- nsLk.lock(ctx, "volume", "path", "source", "opsID", false, 0) 81 }() 82 runtime.Gosched() 83 84 // unlock the manual lock 85 nsLk.lockMapMutex.Unlock() 86 87 // To trigger the race: 88 // 1) lk3 or lk4 need to advance and increment the ref on the existing resource, 89 // successfully acquiring the lock. 90 // 2) lk2 then needs to advance and remove the resource from lockMap. 91 // 3) lk3 or lk4 (whichever didn't execute in step 1) then executes and creates 92 // a new entry in lockMap and acquires a lock for the same resource. 93 94 <-lk2ch 95 lk3ok := <-lk3ch 96 lk4ok := <-lk4ch 97 98 if lk3ok && lk4ok { 99 t.Fatalf("multiple locks acquired; iteration=%d, lk3=%t, lk4=%t", i, lk3ok, lk4ok) 100 } 101 } 102 }