github.com/hyperledger/burrow@v0.34.5-0.20220512172541-77f09336001d/sync/ring_mutex.go (about) 1 package sync 2 3 import ( 4 "sync" 5 6 "hash" 7 8 "encoding/binary" 9 10 "github.com/OneOfOne/xxhash" 11 ) 12 13 type RingMutex struct { 14 mutexes []sync.RWMutex 15 values []Value 16 hash func(address []byte) uint64 17 mutexCount uint64 18 } 19 20 type Value struct { 21 set bool 22 value interface{} 23 } 24 25 func (v *Value) IsSet() bool { 26 return v.set 27 } 28 29 func (v *Value) Set(value interface{}) { 30 v.value = value 31 v.set = true 32 } 33 34 func (v *Value) Get() interface{} { 35 return v.value 36 } 37 38 // Create a RW mutex that provides a pseudo-independent set of mutexes for addresses 39 // where the address space is mapped into possibly much smaller set of backing 40 // mutexes using the xxhash (non-cryptographic) 41 // hash function // modulo size. If some addresses collide modulo size they will be unnecessary 42 // contention between those addresses, but you can trade space against contention 43 // as desired. 44 func NewRingMutex(mutexCount int, hashMaker func() hash.Hash64) *RingMutex { 45 ringMutex := &RingMutex{ 46 mutexCount: uint64(mutexCount), 47 // max slice length is bounded by max(int) thus the argument type 48 mutexes: make([]sync.RWMutex, mutexCount), 49 values: make([]Value, mutexCount), 50 hash: func(address []byte) uint64 { 51 buf := make([]byte, 8) 52 copy(buf, address) 53 return binary.LittleEndian.Uint64(buf) 54 }, 55 } 56 if hashMaker != nil { 57 hasherPool := &sync.Pool{ 58 New: func() interface{} { 59 return hashMaker() 60 }, 61 } 62 ringMutex.hash = func(address []byte) uint64 { 63 h := hasherPool.Get().(hash.Hash64) 64 defer func() { 65 h.Reset() 66 hasherPool.Put(h) 67 }() 68 h.Write(address) 69 return h.Sum64() 70 } 71 } 72 return ringMutex 73 } 74 75 func NewRingMutexNoHash(mutexCount int) *RingMutex { 76 return NewRingMutex(mutexCount, nil) 77 } 78 79 func NewRingMutexXXHash(mutexCount int) *RingMutex { 80 return NewRingMutex(mutexCount, func() hash.Hash64 { 81 return xxhash.New64() 82 }) 83 } 84 85 func (mtx *RingMutex) Lock(address []byte) (value *Value) { 86 index := mtx.index(address) 87 mtx.mutexes[index].Lock() 88 return &mtx.values[index] 89 } 90 91 func (mtx *RingMutex) Unlock(address []byte) { 92 index := mtx.index(address) 93 mtx.mutexes[index].Unlock() 94 } 95 96 func (mtx *RingMutex) RLock(address []byte) { 97 mtx.Mutex(address).RLock() 98 } 99 100 func (mtx *RingMutex) RUnlock(address []byte) { 101 mtx.Mutex(address).RUnlock() 102 } 103 104 // Return the size of the underlying array of mutexes 105 func (mtx *RingMutex) MutexCount() uint64 { 106 return mtx.mutexCount 107 } 108 109 func (mtx *RingMutex) Mutex(address []byte) *sync.RWMutex { 110 return &mtx.mutexes[mtx.index(address)] 111 } 112 113 func (mtx *RingMutex) index(address []byte) uint64 { 114 return mtx.hash(address) % mtx.mutexCount 115 }