github.com/tri-stone/burrow@v0.25.0/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, mutexCount),
    49  		values:  make([]Value, mutexCount, 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  }