Commit eeebfe18 authored by David Vorick's avatar David Vorick

remove demoting locks from sync package

parent 9d0dc410
......@@ -18,8 +18,7 @@ type RWMutex struct {
callDepth int
maxLockTime time.Duration
outer sync.Mutex
inner sync.RWMutex
mu sync.RWMutex
}
// lockInfo contains information about when and how a lock call was made.
......@@ -67,10 +66,9 @@ func (rwm *RWMutex) threadedDeadlockFinder() {
// Undo the deadlock and delete the entry from the map.
if info.read {
rwm.inner.RUnlock()
rwm.mu.RUnlock()
} else {
rwm.inner.Unlock()
rwm.outer.Unlock()
rwm.mu.Unlock()
}
delete(rwm.openLocks, id)
}
......@@ -95,10 +93,9 @@ func (rwm *RWMutex) safeLock(read bool) int {
// Lock the mutex.
if read {
rwm.inner.RLock()
rwm.mu.RLock()
} else {
rwm.outer.Lock()
rwm.inner.Lock()
rwm.mu.Lock()
}
// Safely register that a lock has been triggered.
......@@ -137,29 +134,13 @@ func (rwm *RWMutex) safeUnlock(read bool, id int) {
// Remove the lock and delete the entry from the map.
if read {
rwm.inner.RUnlock()
rwm.mu.RUnlock()
} else {
rwm.inner.Unlock()
rwm.outer.Unlock()
rwm.mu.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 {
......@@ -178,12 +159,6 @@ 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) {
......
......@@ -147,52 +147,3 @@ func TestLockSafety(t *testing.T) {
t.Error("test took too long to complete")
}
}
// TestDemote checks that lock demotion works correctly.
func TestDemote(t *testing.T) {
if testing.Short() {
t.SkipNow()
}
startTime := time.Now().Unix()
value := 0
safeLock := New(time.Second, 1)
_ = safeLock.Lock()
readThreads := 100
var wg sync.WaitGroup
wg.Add(readThreads)
for i := 0; i < readThreads; i++ {
go func() {
readID := safeLock.RLock()
defer safeLock.RUnlock(readID)
if value != 1 {
t.Error("reading is not happening correctly")
}
// Sleep 250 milliseconds after grabbing the readlock. Because
// there are a bunch of threads, if the readlocks are not grabbing
// the lock in parallel the test will take a long time.
time.Sleep(time.Millisecond * 250)
wg.Done()
}()
}
value = 1
// A combination of sleep and gosched to give priority to the other
// threads.
time.Sleep(time.Millisecond * 100)
runtime.Gosched()
time.Sleep(time.Millisecond * 100)
safeLock.Demote()
// Wait for all of the threads to finish sleeping.
wg.Wait()
// Check that the whole test took under 3 seconds. If the readlocks were
// efficiently being grabbed in parallel, the test should be subtantially
// less than 3 seconds.
if time.Now().Unix()-startTime > 3 {
t.Error("test took too long to complete")
}
}
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