gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/host/contractmanager/dependencies_test.go (about) 1 package contractmanager 2 3 import ( 4 "bytes" 5 "os" 6 "path/filepath" 7 "sync" 8 "testing" 9 10 "gitlab.com/NebulousLabs/fastrand" 11 "gitlab.com/SiaPrime/SiaPrime/build" 12 "gitlab.com/SiaPrime/SiaPrime/modules" 13 ) 14 15 // TestParallelFileAccess using a single file handle + ReadAt and WriteAt to 16 // write to multiple locations on a file in parallel, verifying that it's a 17 // safe thing to do. 18 func TestParallelFileAccess(t *testing.T) { 19 if testing.Short() { 20 t.SkipNow() 21 } 22 t.Parallel() 23 24 // Create the file that will be used in parallel. 25 testdir := build.TempDir(modules.ContractManagerDir, "TestParallelFileAccess") 26 err := os.MkdirAll(testdir, 0700) 27 if err != nil { 28 t.Fatal(err) 29 } 30 f, err := os.Create(filepath.Join(testdir, "parallelFile")) 31 if err != nil { 32 t.Fatal(err) 33 } 34 defer f.Close() 35 36 // Create the data that will be writted to the file, such that it can be 37 // verified later. 38 writesPerThread := 200 39 numThreads := 500 40 dataSize := 163 // Intentionally overlaps sector boundaries. 41 datas := make([][]byte, numThreads*writesPerThread) 42 for i := 0; i < numThreads*writesPerThread; i++ { 43 datas[i] = make([]byte, dataSize) 44 fastrand.Read(datas[i]) 45 } 46 47 // Spin up threads to make concurrent writes to the file in different 48 // locations. Have some reads + writes that are trying to overlap. 49 threadingModifier := 71 50 var wg1 sync.WaitGroup 51 var wg2 sync.WaitGroup 52 for i := 0; i < numThreads; i++ { 53 if i%threadingModifier == 0 { 54 wg1.Add(1) 55 } else { 56 wg2.Add(1) 57 } 58 go func(i int) { 59 if i%threadingModifier == 0 { 60 defer wg1.Done() 61 } else { 62 defer wg2.Done() 63 } 64 65 for j := 0; j < writesPerThread; j++ { 66 _, err := f.WriteAt(datas[i*j], int64(i*dataSize*j)) 67 if err != nil { 68 t.Error(err) 69 } 70 } 71 }(i) 72 } 73 // Wait for the smaller set of first writes to complete. 74 wg1.Wait() 75 76 // Verify the results for the smaller set of writes. 77 for i := 0; i < numThreads; i++ { 78 if i%threadingModifier != 0 { 79 continue 80 } 81 wg1.Add(1) 82 go func(i int) { 83 defer wg1.Done() 84 for j := 0; j < writesPerThread; j++ { 85 data := make([]byte, dataSize) 86 _, err := f.ReadAt(data, int64(i*dataSize)) 87 if err != nil { 88 t.Error(err) 89 } 90 if !bytes.Equal(data, datas[i]) { 91 t.Error("data mismatch for value", i) 92 } 93 } 94 }(i) 95 } 96 wg1.Wait() 97 wg2.Wait() 98 99 // Verify the results for all of the writes. 100 for i := 0; i < numThreads; i++ { 101 wg1.Add(1) 102 go func(i int) { 103 defer wg1.Done() 104 for j := 0; j < writesPerThread; j++ { 105 data := make([]byte, dataSize) 106 _, err := f.ReadAt(data, int64(i*dataSize)) 107 if err != nil { 108 t.Error(err) 109 } 110 if !bytes.Equal(data, datas[i]) { 111 t.Error("data mismatch for value", i) 112 } 113 } 114 }(i) 115 } 116 wg1.Wait() 117 }