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  }