github.com/Synthesix/Sia@v1.3.3-0.20180413141344-f863baeed3ca/modules/renter/proto/contractset_test.go (about)

     1  package proto
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/Synthesix/Sia/types"
     9  
    10  	"github.com/NebulousLabs/fastrand"
    11  )
    12  
    13  // mustAcquire is a convenience function for acquiring contracts that are
    14  // known to be in the set.
    15  func (cs *ContractSet) mustAcquire(t *testing.T, id types.FileContractID) *SafeContract {
    16  	t.Helper()
    17  	c, ok := cs.Acquire(id)
    18  	if !ok {
    19  		t.Fatal("no contract with that id")
    20  	}
    21  	return c
    22  }
    23  
    24  // TestContractSet tests that the ContractSet type is safe for concurrent use.
    25  func TestContractSet(t *testing.T) {
    26  	// create contract set
    27  	c1 := &SafeContract{header: contractHeader{Transaction: types.Transaction{
    28  		FileContractRevisions: []types.FileContractRevision{{
    29  			ParentID:             types.FileContractID{1},
    30  			NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
    31  			UnlockConditions: types.UnlockConditions{
    32  				PublicKeys: []types.SiaPublicKey{{}, {}},
    33  			},
    34  		}},
    35  	}}}
    36  	id1 := c1.header.ID()
    37  	c2 := &SafeContract{header: contractHeader{Transaction: types.Transaction{
    38  		FileContractRevisions: []types.FileContractRevision{{
    39  			ParentID:             types.FileContractID{2},
    40  			NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
    41  			UnlockConditions: types.UnlockConditions{
    42  				PublicKeys: []types.SiaPublicKey{{}, {}},
    43  			},
    44  		}},
    45  	}}}
    46  	id2 := c2.header.ID()
    47  	cs := &ContractSet{
    48  		contracts: map[types.FileContractID]*SafeContract{
    49  			id1: c1,
    50  			id2: c2,
    51  		},
    52  	}
    53  
    54  	// uncontested acquire/release
    55  	c1 = cs.mustAcquire(t, id1)
    56  	cs.Return(c1)
    57  
    58  	// 100 concurrent serialized mutations
    59  	var wg sync.WaitGroup
    60  	for i := 0; i < 100; i++ {
    61  		wg.Add(1)
    62  		go func() {
    63  			defer wg.Done()
    64  			c1 := cs.mustAcquire(t, id1)
    65  			c1.header.Transaction.FileContractRevisions[0].NewRevisionNumber++
    66  			time.Sleep(time.Duration(fastrand.Intn(100)))
    67  			cs.Return(c1)
    68  		}()
    69  	}
    70  	wg.Wait()
    71  	c1 = cs.mustAcquire(t, id1)
    72  	cs.Return(c1)
    73  	if c1.header.LastRevision().NewRevisionNumber != 100 {
    74  		t.Fatal("expected exactly 100 increments, got", c1.header.LastRevision().NewRevisionNumber)
    75  	}
    76  
    77  	// a blocked acquire shouldn't prevent a return
    78  	c1 = cs.mustAcquire(t, id1)
    79  	go func() {
    80  		time.Sleep(time.Millisecond)
    81  		cs.Return(c1)
    82  	}()
    83  	c1 = cs.mustAcquire(t, id1)
    84  	cs.Return(c1)
    85  
    86  	// delete and reinsert id2
    87  	c2 = cs.mustAcquire(t, id2)
    88  	cs.Delete(c2)
    89  	cs.mu.Lock()
    90  	cs.contracts[id2] = c2
    91  	cs.mu.Unlock()
    92  
    93  	// call all the methods in parallel haphazardly
    94  	funcs := []func(){
    95  		func() { cs.Len() },
    96  		func() { cs.IDs() },
    97  		func() { cs.View(id1); cs.View(id2) },
    98  		func() { cs.ViewAll() },
    99  		func() { cs.Return(cs.mustAcquire(t, id1)) },
   100  		func() { cs.Return(cs.mustAcquire(t, id2)) },
   101  		func() {
   102  			c3 := &SafeContract{header: contractHeader{
   103  				Transaction: types.Transaction{
   104  					FileContractRevisions: []types.FileContractRevision{{
   105  						ParentID:             types.FileContractID{3},
   106  						NewValidProofOutputs: []types.SiacoinOutput{{}, {}},
   107  						UnlockConditions: types.UnlockConditions{
   108  							PublicKeys: []types.SiaPublicKey{{}, {}},
   109  						},
   110  					}},
   111  				},
   112  			}}
   113  			id3 := c3.header.ID()
   114  			cs.mu.Lock()
   115  			cs.contracts[id3] = c3
   116  			cs.mu.Unlock()
   117  			cs.mustAcquire(t, id3)
   118  			cs.Delete(c3)
   119  		},
   120  	}
   121  	wg = sync.WaitGroup{}
   122  	for _, fn := range funcs {
   123  		wg.Add(1)
   124  		go func(fn func()) {
   125  			defer wg.Done()
   126  			for i := 0; i < 100; i++ {
   127  				time.Sleep(time.Duration(fastrand.Intn(100)))
   128  				fn()
   129  			}
   130  		}(fn)
   131  	}
   132  	wg.Wait()
   133  }