diff options
Diffstat (limited to 'vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go')
-rw-r--r-- | vendor/github.com/puzpuzpuz/xsync/v3/rbmutex.go | 188 |
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() -} |