summaryrefslogtreecommitdiff
path: root/vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go')
-rw-r--r--vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go188
1 files changed, 0 insertions, 188 deletions
diff --git a/vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go b/vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go
deleted file mode 100644
index 4cbd9c41d..000000000
--- a/vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go
+++ /dev/null
@@ -1,188 +0,0 @@
-package xsync
-
-import (
- "runtime"
- "sync"
- "sync/atomic"
- "time"
-)
-
-// slow-down guard
-const nslowdown = 7
-
-// pool for reader tokens
-var rtokenPool sync.Pool
-
-// RToken is a reader lock token.
-type RToken struct {
- slot uint32
- //lint:ignore U1000 prevents false sharing
- pad [cacheLineSize - 4]byte
-}
-
-// A RBMutex is a reader biased reader/writer mutual exclusion lock.
-// The lock can be held by an many readers or a single writer.
-// The zero value for a RBMutex is an unlocked mutex.
-//
-// A RBMutex must not be copied after first use.
-//
-// RBMutex is based on a modified version of BRAVO
-// (Biased Locking for Reader-Writer Locks) algorithm:
-// https://arxiv.org/pdf/1810.01553.pdf
-//
-// RBMutex is a specialized mutex for scenarios, such as caches,
-// where the vast majority of locks are acquired by readers and write
-// lock acquire attempts are infrequent. In such scenarios, RBMutex
-// performs better than sync.RWMutex on large multicore machines.
-//
-// RBMutex extends sync.RWMutex internally and uses it as the "reader
-// bias disabled" fallback, so the same semantics apply. The only
-// noticeable difference is in reader tokens returned from the
-// RLock/RUnlock methods.
-type RBMutex struct {
- rslots []rslot
- rmask uint32
- rbias int32
- inhibitUntil time.Time
- rw sync.RWMutex
-}
-
-type rslot struct {
- mu int32
- //lint:ignore U1000 prevents false sharing
- pad [cacheLineSize - 4]byte
-}
-
-// NewRBMutex creates a new RBMutex instance.
-func NewRBMutex() *RBMutex {
- nslots := nextPowOf2(parallelism())
- mu := RBMutex{
- rslots: make([]rslot, nslots),
- rmask: nslots - 1,
- rbias: 1,
- }
- return &mu
-}
-
-// TryRLock tries to lock m for reading without blocking.
-// When TryRLock succeeds, it returns true and a reader token.
-// In case of a failure, a false is returned.
-func (mu *RBMutex) TryRLock() (bool, *RToken) {
- if t := mu.fastRlock(); t != nil {
- return true, t
- }
- // Optimistic slow path.
- if mu.rw.TryRLock() {
- if atomic.LoadInt32(&mu.rbias) == 0 && time.Now().After(mu.inhibitUntil) {
- atomic.StoreInt32(&mu.rbias, 1)
- }
- return true, nil
- }
- return false, nil
-}
-
-// RLock locks m for reading and returns a reader token. The
-// token must be used in the later RUnlock call.
-//
-// Should not be used for recursive read locking; a blocked Lock
-// call excludes new readers from acquiring the lock.
-func (mu *RBMutex) RLock() *RToken {
- if t := mu.fastRlock(); t != nil {
- return t
- }
- // Slow path.
- mu.rw.RLock()
- if atomic.LoadInt32(&mu.rbias) == 0 && time.Now().After(mu.inhibitUntil) {
- atomic.StoreInt32(&mu.rbias, 1)
- }
- return nil
-}
-
-func (mu *RBMutex) fastRlock() *RToken {
- if atomic.LoadInt32(&mu.rbias) == 1 {
- t, ok := rtokenPool.Get().(*RToken)
- if !ok {
- t = new(RToken)
- t.slot = runtime_fastrand()
- }
- // Try all available slots to distribute reader threads to slots.
- for i := 0; i < len(mu.rslots); i++ {
- slot := t.slot + uint32(i)
- rslot := &mu.rslots[slot&mu.rmask]
- rslotmu := atomic.LoadInt32(&rslot.mu)
- if atomic.CompareAndSwapInt32(&rslot.mu, rslotmu, rslotmu+1) {
- if atomic.LoadInt32(&mu.rbias) == 1 {
- // Hot path succeeded.
- t.slot = slot
- return t
- }
- // The mutex is no longer reader biased. Roll back.
- atomic.AddInt32(&rslot.mu, -1)
- rtokenPool.Put(t)
- return nil
- }
- // Contention detected. Give a try with the next slot.
- }
- }
- return nil
-}
-
-// RUnlock undoes a single RLock call. A reader token obtained from
-// the RLock call must be provided. RUnlock does not affect other
-// simultaneous readers. A panic is raised if m is not locked for
-// reading on entry to RUnlock.
-func (mu *RBMutex) RUnlock(t *RToken) {
- if t == nil {
- mu.rw.RUnlock()
- return
- }
- if atomic.AddInt32(&mu.rslots[t.slot&mu.rmask].mu, -1) < 0 {
- panic("invalid reader state detected")
- }
- rtokenPool.Put(t)
-}
-
-// TryLock tries to lock m for writing without blocking.
-func (mu *RBMutex) TryLock() bool {
- if mu.rw.TryLock() {
- if atomic.LoadInt32(&mu.rbias) == 1 {
- atomic.StoreInt32(&mu.rbias, 0)
- for i := 0; i < len(mu.rslots); i++ {
- if atomic.LoadInt32(&mu.rslots[i].mu) > 0 {
- // There is a reader. Roll back.
- atomic.StoreInt32(&mu.rbias, 1)
- mu.rw.Unlock()
- return false
- }
- }
- }
- return true
- }
- return false
-}
-
-// Lock locks m for writing. If the lock is already locked for
-// reading or writing, Lock blocks until the lock is available.
-func (mu *RBMutex) Lock() {
- mu.rw.Lock()
- if atomic.LoadInt32(&mu.rbias) == 1 {
- atomic.StoreInt32(&mu.rbias, 0)
- start := time.Now()
- for i := 0; i < len(mu.rslots); i++ {
- for atomic.LoadInt32(&mu.rslots[i].mu) > 0 {
- runtime.Gosched()
- }
- }
- mu.inhibitUntil = time.Now().Add(time.Since(start) * nslowdown)
- }
-}
-
-// Unlock unlocks m for writing. A panic is raised if m is not locked
-// for writing on entry to Unlock.
-//
-// As with RWMutex, a locked RBMutex is not associated with a
-// particular goroutine. One goroutine may RLock (Lock) a RBMutex and
-// then arrange for another goroutine to RUnlock (Unlock) it.
-func (mu *RBMutex) Unlock() {
- mu.rw.Unlock()
-}