github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/util/syncutil/int_map_test.go (about)

     1  // Copyright 2019 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  // Copyright 2016 The Go Authors. All rights reserved.
    12  // Use of this source code is governed by a BSD-style
    13  // license that can be found in licenses/BSD-golang.txt.
    14  
    15  // This code originated in Go's sync package.
    16  
    17  package syncutil
    18  
    19  import (
    20  	"math/rand"
    21  	"reflect"
    22  	"runtime"
    23  	"sync"
    24  	"testing"
    25  	"testing/quick"
    26  	"unsafe"
    27  )
    28  
    29  type mapOp string
    30  
    31  const (
    32  	opLoad        = mapOp("Load")
    33  	opStore       = mapOp("Store")
    34  	opLoadOrStore = mapOp("LoadOrStore")
    35  	opDelete      = mapOp("Delete")
    36  )
    37  
    38  var mapOps = [...]mapOp{opLoad, opStore, opLoadOrStore, opDelete}
    39  
    40  // mapCall is a quick.Generator for calls on mapInterface.
    41  type mapCall struct {
    42  	op mapOp
    43  	k  int64
    44  	v  unsafe.Pointer
    45  }
    46  
    47  func (c mapCall) apply(m mapInterface) (unsafe.Pointer, bool) {
    48  	switch c.op {
    49  	case opLoad:
    50  		return m.Load(c.k)
    51  	case opStore:
    52  		m.Store(c.k, c.v)
    53  		return nil, false
    54  	case opLoadOrStore:
    55  		return m.LoadOrStore(c.k, c.v)
    56  	case opDelete:
    57  		m.Delete(c.k)
    58  		return nil, false
    59  	default:
    60  		panic("invalid mapOp")
    61  	}
    62  }
    63  
    64  type mapResult struct {
    65  	value interface{}
    66  	ok    bool
    67  }
    68  
    69  func randValue(r *rand.Rand) unsafe.Pointer {
    70  	i := new(int)
    71  	*i = r.Int()
    72  	return unsafe.Pointer(i)
    73  }
    74  
    75  func (mapCall) Generate(r *rand.Rand, size int) reflect.Value {
    76  	c := mapCall{op: mapOps[rand.Intn(len(mapOps))], k: r.Int63()}
    77  	switch c.op {
    78  	case opStore, opLoadOrStore:
    79  		c.v = randValue(r)
    80  	}
    81  	return reflect.ValueOf(c)
    82  }
    83  
    84  func applyCalls(
    85  	m mapInterface, calls []mapCall,
    86  ) (results []mapResult, final map[interface{}]interface{}) {
    87  	for _, c := range calls {
    88  		v, ok := c.apply(m)
    89  		results = append(results, mapResult{v, ok})
    90  	}
    91  
    92  	final = make(map[interface{}]interface{})
    93  	m.Range(func(k int64, v unsafe.Pointer) bool {
    94  		final[k] = v
    95  		return true
    96  	})
    97  
    98  	return results, final
    99  }
   100  
   101  func applyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) {
   102  	return applyCalls(new(IntMap), calls)
   103  }
   104  
   105  func applyRWMutexMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) {
   106  	return applyCalls(new(RWMutexMap), calls)
   107  }
   108  
   109  func applyDeepCopyMap(calls []mapCall) ([]mapResult, map[interface{}]interface{}) {
   110  	return applyCalls(new(DeepCopyMap), calls)
   111  }
   112  
   113  func TestMapMatchesRWMutex(t *testing.T) {
   114  	if err := quick.CheckEqual(applyMap, applyRWMutexMap, nil); err != nil {
   115  		t.Error(err)
   116  	}
   117  }
   118  
   119  func TestMapMatchesDeepCopy(t *testing.T) {
   120  	if err := quick.CheckEqual(applyMap, applyDeepCopyMap, nil); err != nil {
   121  		t.Error(err)
   122  	}
   123  }
   124  
   125  func TestConcurrentRange(t *testing.T) {
   126  	const mapSize = 1 << 10
   127  
   128  	m := new(IntMap)
   129  	for n := int64(1); n <= mapSize; n++ {
   130  		v := new(int64)
   131  		*v = n
   132  		m.Store(n, unsafe.Pointer(v))
   133  	}
   134  
   135  	done := make(chan struct{})
   136  	var wg sync.WaitGroup
   137  	defer func() {
   138  		close(done)
   139  		wg.Wait()
   140  	}()
   141  	for g := int64(runtime.GOMAXPROCS(0)); g > 0; g-- {
   142  		r := rand.New(rand.NewSource(g))
   143  		wg.Add(1)
   144  		go func(g int64) {
   145  			defer wg.Done()
   146  			for i := int64(0); ; i++ {
   147  				select {
   148  				case <-done:
   149  					return
   150  				default:
   151  				}
   152  				for n := int64(1); n < mapSize; n++ {
   153  					if r.Int63n(mapSize) == 0 {
   154  						v := new(int64)
   155  						*v = n * i * g
   156  						m.Store(n, unsafe.Pointer(v))
   157  					} else {
   158  						m.Load(n)
   159  					}
   160  				}
   161  			}
   162  		}(g)
   163  	}
   164  
   165  	iters := 1 << 10
   166  	if testing.Short() {
   167  		iters = 16
   168  	}
   169  	for n := iters; n > 0; n-- {
   170  		seen := make(map[int64]bool, mapSize)
   171  
   172  		m.Range(func(ki int64, vi unsafe.Pointer) bool {
   173  			k, v := ki, *(*int64)(vi)
   174  			if v%k != 0 {
   175  				t.Fatalf("while Storing multiples of %v, Range saw value %v", k, v)
   176  			}
   177  			if seen[k] {
   178  				t.Fatalf("Range visited key %v twice", k)
   179  			}
   180  			seen[k] = true
   181  			return true
   182  		})
   183  
   184  		if len(seen) != mapSize {
   185  			t.Fatalf("Range visited %v elements of %v-element Map", len(seen), mapSize)
   186  		}
   187  	}
   188  }