Commit 22d46d53 authored by Dmitriy Vyukov's avatar Dmitriy Vyukov

sync: detect incorrect usages of RWMutex

Fixes #7858.

LGTM=ruiu
R=ruiu
CC=golang-codereviews
https://golang.org/cl/92720045
parent 5ce98da1
......@@ -51,7 +51,11 @@ func (rw *RWMutex) RUnlock() {
raceReleaseMerge(unsafe.Pointer(&rw.writerSem))
raceDisable()
}
if atomic.AddInt32(&rw.readerCount, -1) < 0 {
if r := atomic.AddInt32(&rw.readerCount, -1); r < 0 {
if r+1 == 0 || r+1 == -rwmutexMaxReaders {
raceEnable()
panic("sync: RUnlock of unlocked RWMutex")
}
// A writer is pending.
if atomic.AddInt32(&rw.readerWait, -1) == 0 {
// The last reader unblocks the writer.
......@@ -105,6 +109,10 @@ func (rw *RWMutex) Unlock() {
// Announce to readers there is no active writer.
r := atomic.AddInt32(&rw.readerCount, rwmutexMaxReaders)
if r >= rwmutexMaxReaders {
raceEnable()
panic("sync: Unlock of unlocked RWMutex")
}
// Unblock blocked readers, if any.
for i := 0; i < int(r); i++ {
runtime_Semrelease(&rw.readerSem)
......
......@@ -155,6 +155,48 @@ func TestRLocker(t *testing.T) {
}
}
func TestUnlockPanic(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatalf("unlock of unlocked RWMutex did not panic")
}
}()
var mu RWMutex
mu.Unlock()
}
func TestUnlockPanic2(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatalf("unlock of unlocked RWMutex did not panic")
}
}()
var mu RWMutex
mu.RLock()
mu.Unlock()
}
func TestRUnlockPanic(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatalf("read unlock of unlocked RWMutex did not panic")
}
}()
var mu RWMutex
mu.RUnlock()
}
func TestRUnlockPanic2(t *testing.T) {
defer func() {
if recover() == nil {
t.Fatalf("read unlock of unlocked RWMutex did not panic")
}
}()
var mu RWMutex
mu.Lock()
mu.RUnlock()
}
func BenchmarkRWMutexUncontended(b *testing.B) {
type PaddedRWMutex struct {
RWMutex
......
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