You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gophi/core/mutex.go

69 lines
1.7 KiB
Go

package core
import (
"sync"
"sync/atomic"
"time"
)
// UpgradeableMutex wraps a RWMutex and provides safe upgrading / downgrading
// by informing you whether a write lock was achieved in the brief swap time
type UpgradeableMutex struct {
wLast int64
internal sync.RWMutex
}
// RLock exactly wraps the internal RWMutex
func (mu *UpgradeableMutex) RLock() {
mu.internal.RLock()
}
// RUnlock exactly wraps the internal RWMutex
func (mu *UpgradeableMutex) RUnlock() {
mu.internal.RUnlock()
}
// Lock wraps the internal RWMutex, atomically storing the last write-lock time
func (mu *UpgradeableMutex) Lock() {
mu.internal.Lock()
atomic.StoreInt64(&mu.wLast, time.Now().UnixNano())
}
// Unlock exactly wraps the internal RWMutex
func (mu *UpgradeableMutex) Unlock() {
mu.internal.Unlock()
}
// safeSwap stores the current time, performs the swap function and checks if
// any write locks were achieved during the swap function
func (mu *UpgradeableMutex) safeSwap(swapFn func()) bool {
// Get the 'now' time
now := time.Now().UnixNano()
// Store now time
atomic.StoreInt64(&mu.wLast, now)
// Perform the swap
swapFn()
// Successful swap determined by if last write-lock
// is still equal to 'now'
return atomic.LoadInt64(&mu.wLast) == now
}
// UpgradeLock upgrades a read to a write lock, returning success state as a bool
func (mu *UpgradeableMutex) UpgradeLock() bool {
return mu.safeSwap(func() {
mu.internal.RUnlock()
mu.internal.Lock()
})
}
// DowngradeLock downgrades a write to a read lock, returning success state as a bool
func (mu *UpgradeableMutex) DowngradeLock() bool {
return mu.safeSwap(func() {
mu.internal.Unlock()
mu.internal.RLock()
})
}