kaspad/util/locks/prioritymutex.go
Ori Newman 8e0e62f21a [NOD-447] fix deadlocks and hanging goroutines (#481)
* [NOD-447] Fix deadlocks and hanging goroutines

* [NOD-447] Add tests

* [NOD-447] Add unpatch to spawnPatch

* [NOD-447] Don't send to releaseWait if waitingCounter is zero

* [NOD-447] Change waitingCounter to boolean and rename to isReleaseWaitWaiting, change checkIfRunningSpawnsAreLeft to return only one function, and lock critical code related to wg.isReleaseWaitWaiting

* [NOD-447] Rename txConfirmations -> txConfirmationsNoLock, txConfirmationsWithLock -> txConfirmations

* [NOD-447] Add documentation and delete redundant spawn

* [NOD-447] Fix comments

* [NOD-447] Fix comments
2019-11-24 15:59:45 +02:00

80 lines
2.5 KiB
Go

package locks
import (
"sync"
)
// PriorityMutex is a read-write mutex with an additional low
// priority lock. It's implemented with the following
// components:
// * Data mutex: The actual lock on the data structure. Its
// type is sync.RWMutex for its high priority read lock.
// * High priority waiting group: A waiting group that is being
// incremented every time a high priority lock (read or write)
// is acquired, and decremented every time a high priority lock is
// unlocked. Low priority locks can start being held only
// when the waiting group is empty.
// * Low priority mutex: This mutex ensures that when the
// waiting group is empty, only one low priority lock
// will be able to lock the data mutex.
// PriorityMutex implements a lock with three priorities:
// * High priority write lock - locks the mutex with the highest priority.
// * High priority read lock - locks the mutex with lower priority than
// the high priority write lock. Can be held concurrently with other
// with other read locks.
// * Low priority write lock - locks the mutex with lower priority then
// the read lock.
type PriorityMutex struct {
dataMutex sync.RWMutex
highPriorityWaiting *waitGroup
lowPriorityMutex sync.Mutex
}
// NewPriorityMutex returns a new priority mutex
func NewPriorityMutex() *PriorityMutex {
lock := PriorityMutex{
highPriorityWaiting: newWaitGroup(),
}
return &lock
}
// LowPriorityWriteLock acquires a low-priority write lock.
func (mtx *PriorityMutex) LowPriorityWriteLock() {
mtx.lowPriorityMutex.Lock()
mtx.highPriorityWaiting.wait()
mtx.dataMutex.Lock()
}
// LowPriorityWriteUnlock unlocks the low-priority write lock
func (mtx *PriorityMutex) LowPriorityWriteUnlock() {
mtx.dataMutex.Unlock()
mtx.lowPriorityMutex.Unlock()
}
// HighPriorityWriteLock acquires a high-priority write lock.
func (mtx *PriorityMutex) HighPriorityWriteLock() {
mtx.highPriorityWaiting.add(1)
mtx.dataMutex.Lock()
}
// HighPriorityWriteUnlock unlocks the high-priority write lock
func (mtx *PriorityMutex) HighPriorityWriteUnlock() {
mtx.dataMutex.Unlock()
mtx.highPriorityWaiting.done()
}
// HighPriorityReadLock acquires a high-priority read
// lock.
func (mtx *PriorityMutex) HighPriorityReadLock() {
mtx.highPriorityWaiting.add(1)
mtx.dataMutex.RLock()
}
// HighPriorityReadUnlock unlocks the high-priority read
// lock
func (mtx *PriorityMutex) HighPriorityReadUnlock() {
mtx.highPriorityWaiting.done()
mtx.dataMutex.RUnlock()
}