sq/libsq/core/upgrademu/upgrademu.go
Neil O'Toole a3cd01f36a
#353 Diff performance (#399)
* Diff refactor
2024-02-20 16:26:45 -07:00

199 lines
6.4 KiB
Go

// Based on source code copyright by The Go Authors.
//
// Copyright (c) 2009 The Go Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package upgrademu
import (
"sync"
"sync/atomic"
_ "unsafe"
)
// RWMutex is an enhanced version of the standard sync.RWMutex.
// It has the all methods sync.RWMutex with exact same semantics.
// It gives more methods to give upgradable-read feature.
//
// The new semantics for upgradable-read are as follows:
// Multiple goroutines can get read-lock together with a single upgradable-read-lock.
// Only one goroutine can have a write-lock and no read-lock/upgradable-read-lock can be acquired in this state.
// There can be only a single goroutine keeping the upgrade-read-lock.
// RWMutex is not reentrant.
//
// Usage of the RWMutex:
//
// mutex.UpgradableRLock()
// defer mutex.UpgradableRUnlock()
// // read-lock acquired section. We can return here safely if an error occurs
// mutex.UpgradeWLock()
// // critical section with exclusive right access
type RWMutex struct {
w sync.Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount atomic.Int32 // number of pending readers
// number of departing readers. A negative number.
// Number of readers left while under write lock(while write lock waiting for readers to leave)
readerWait atomic.Int32
// Keep track if an upgradeable read-lock is upgraded to write-lock or not. Always accessed under w locked
upgraded bool
upgradableReadMode bool
}
//go:linkname semaphoreAcquire sync.runtime_Semacquire
func semaphoreAcquire(s *uint32)
//go:linkname semaphoreRelease sync.runtime_Semrelease
func semaphoreRelease(s *uint32, handoff bool, skipframes int)
const rwmutexMaxReaders = 1 << 30
// RLock is same as sync.RWMutex.RLock
func (rw *RWMutex) RLock() {
if rw.readerCount.Add(1) < 0 {
// A writer is pending, wait for it.
semaphoreAcquire(&rw.readerSem)
}
}
// TryRLock is same as sync.RWMutex
func (rw *RWMutex) TryRLock() bool {
for {
c := rw.readerCount.Load()
if c < 0 {
return false
}
if rw.readerCount.CompareAndSwap(c, c+1) {
return true
}
}
}
// RUnlock is same as sync.RWMutex
func (rw *RWMutex) RUnlock() {
if r := rw.readerCount.Add(-1); r < 0 {
// Outlined slow-path to allow the fast-path to be inlined
rw.rUnlockSlow(r)
}
}
func (rw *RWMutex) rUnlockSlow(r int32) {
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
panic("sync: RUnlock of unlocked RWMutex")
}
// A writer is pending.
if rw.readerWait.Add(-1) == 0 {
// The last reader unblocks the writer.
semaphoreRelease(&rw.writerSem, false, 1)
}
}
// Lock is same as sync.RWMutex
func (rw *RWMutex) Lock() {
// First, resolve competition with other writers.
rw.w.Lock()
// Announce to readers there is a pending writer.
r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && rw.readerWait.Add(r) != 0 {
semaphoreAcquire(&rw.writerSem)
}
}
// TryLock is same as sync.RWMutex
func (rw *RWMutex) TryLock() bool {
if !rw.w.TryLock() {
return false
}
if !rw.readerCount.CompareAndSwap(0, -rwmutexMaxReaders) {
rw.w.Unlock()
return false
}
return true
}
// Unlock is same as sync.RWMutex
func (rw *RWMutex) Unlock() {
// Announce to readers there is no active writer.
r := rw.readerCount.Add(rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
panic("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
for i := 0; i < int(r); i++ {
semaphoreRelease(&rw.readerSem, false, 0)
}
// Allow other writers to proceed.
rw.w.Unlock()
}
// UpgradeWLock upgrade the read lock to the write lock
func (rw *RWMutex) UpgradeWLock() {
if !rw.upgradableReadMode {
panic("sync: Upgrade outside of upgradableReadLock not allowed")
}
rw.upgraded = true
// Announce to readers there is a pending writer.
r := rw.readerCount.Add(-rwmutexMaxReaders) + rwmutexMaxReaders
// Wait for active readers.
if r != 0 && rw.readerWait.Add(r) != 0 {
semaphoreAcquire(&rw.writerSem)
}
}
// UpgradableRUnlock unlocks either the write-lock if it is upgraded
// or unlock just the upgradeableRead-lock if not upgraded
func (rw *RWMutex) UpgradableRUnlock() {
rw.upgradableReadMode = false
if rw.upgraded {
rw.upgraded = false
rw.Unlock()
} else {
rw.w.Unlock()
}
}
// UpgradableRLock acquires an upgradable-read-lock which can be later upgraded to write-lock,
// Example usage:
//
// mutex.UpgradableRLock()
// defer mutex.UpgradableRUnlock()
// // read-lock acquired section. We can return here safely if an error occurs
// mutex.UpgradeWLock()
// // critical section with exclusive right access
func (rw *RWMutex) UpgradableRLock() {
// First, resolve competition with other writers.
// Disallow writers to acquire the lock
rw.w.Lock()
if rw.readerCount.Load() < 0 {
panic("reader count can not be negative. We have the write lock")
}
rw.upgradableReadMode = true
}