Commit 1894496d authored by David Vorick's avatar David Vorick

untested demote implementation for safelocks

parent 1f54f7af
......@@ -18,7 +18,8 @@ type RWMutex struct {
callDepth int
maxLockTime time.Duration
mu sync.RWMutex
outer sync.Mutex
inner sync.RWMutex
}
// lockInfo contains information about when and how a lock call was made.
......@@ -66,9 +67,10 @@ func (rwm *RWMutex) threadedDeadlockFinder() {
// Undo the deadlock and delete the entry from the map.
if info.read {
rwm.mu.RUnlock()
rwm.inner.RUnlock()
} else {
rwm.mu.Unlock()
rwm.inner.Unlock()
rwm.outer.Unlock()
}
delete(rwm.openLocks, id)
}
......@@ -93,9 +95,10 @@ func (rwm *RWMutex) safeLock(read bool) int {
// Lock the mutex.
if read {
rwm.mu.RLock()
rwm.inner.RLock()
} else {
rwm.mu.Lock()
rwm.outer.Lock()
rwm.inner.Lock()
}
// Safely register that a lock has been triggered.
......@@ -112,19 +115,19 @@ func (rwm *RWMutex) safeLock(read bool) int {
// safeUnlock is the generic function for doing safe unlocking. If the lock had
// to be removed because a deadlock was detected, an error is printed.
func (rwm *RWMutex) safeUnlock(read bool, id int) {
// Get the call stack.
callingFiles := make([]string, rwm.callDepth+1)
callingLines := make([]int, rwm.callDepth+1)
for i := 0; i <= rwm.callDepth; i++ {
_, callingFiles[i], callingLines[i], _ = runtime.Caller(2 + i)
}
rwm.openLocksMutex.Lock()
defer rwm.openLocksMutex.Unlock()
// Check if a deadlock has been detected and fixed manually.
_, exists := rwm.openLocks[id]
if !exists {
// Get the call stack.
callingFiles := make([]string, rwm.callDepth+1)
callingLines := make([]int, rwm.callDepth+1)
for i := 0; i <= rwm.callDepth; i++ {
_, callingFiles[i], callingLines[i], _ = runtime.Caller(2 + i)
}
fmt.Printf("A lock was held until deadlock, subsequent call to unlock failed. id '%v'. Call stack:\n", id)
for i := 0; i <= rwm.callDepth; i++ {
fmt.Printf("\tFile: '%v:%v'\n", callingFiles[i], callingLines[i])
......@@ -132,15 +135,31 @@ func (rwm *RWMutex) safeUnlock(read bool, id int) {
return
}
// Remove the lock and delete the netry from the map.
// Remove the lock and delete the entry from the map.
if read {
rwm.mu.RUnlock()
rwm.inner.RUnlock()
} else {
rwm.mu.Unlock()
rwm.inner.Unlock()
rwm.outer.Unlock()
}
delete(rwm.openLocks, id)
}
// safeDemote demotes a safelock from a Lock to a RLock.
func (rwm *RWMutex) safeDemote() {
rwm.openLocksMutex.Lock()
defer rwm.openLocksMutex.Unlock()
// Demote the lock. Ordering is important. First the inner lock is released
// and then regrabbed as a readlock. Then the outer lock is released.
// Because no safelock can grab the inner lock as a writelock until it has
// the outer lock, there is no risk of changes being made during the
// transition.
rwm.inner.Unlock()
rwm.inner.RLock()
rwm.outer.Unlock()
}
// RLock will read lock the RWMutex. The return value must be used as input
// when calling RUnlock.
func (rwm *RWMutex) RLock() int {
......@@ -159,6 +178,12 @@ func (rwm *RWMutex) Lock() int {
return rwm.safeLock(false)
}
// Demote will demote the lock from a writelock to a readlock. Demote should
// only be called on a writelocked safelock.
func (rwm *RWMutex) Demote() {
rwm.safeDemote()
}
// Unlock will unlock the RWMutex. The return value of calling Lock must be
// used as input.
func (rwm *RWMutex) Unlock(id int) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment