Add trylock support to DowngradableRWMutex.

Updates #231

PiperOrigin-RevId: 290868875
This commit is contained in:
Ian Gudger
2020-01-21 19:11:18 -08:00
committed by gVisor bot
parent 1effdc091b
commit d0e75f2bef
2 changed files with 115 additions and 8 deletions
+55
View File
@@ -148,3 +148,58 @@ func TestDowngradableRWMutex(t *testing.T) {
HammerDowngradableRWMutex(10, 10, n)
HammerDowngradableRWMutex(10, 5, n)
}
func TestRWDoubleTryLock(t *testing.T) {
var m DowngradableRWMutex
if !m.TryLock() {
t.Fatal("failed to aquire lock")
}
if m.TryLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestRWTryLockAfterLock(t *testing.T) {
var m DowngradableRWMutex
m.Lock()
if m.TryLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestRWTryLockUnlock(t *testing.T) {
var m DowngradableRWMutex
if !m.TryLock() {
t.Fatal("failed to aquire lock")
}
m.Unlock()
if !m.TryLock() {
t.Fatal("failed to aquire lock after unlock")
}
}
func TestTryRLockAfterLock(t *testing.T) {
var m DowngradableRWMutex
m.Lock()
if m.TryRLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestTryLockAfterRLock(t *testing.T) {
var m DowngradableRWMutex
m.RLock()
if m.TryLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestDoubleTryRLock(t *testing.T) {
var m DowngradableRWMutex
if !m.TryRLock() {
t.Fatal("failed to aquire lock")
}
if !m.TryRLock() {
t.Fatal("failed to read aquire read locked lock")
}
}
+60 -8
View File
@@ -19,7 +19,6 @@
package sync
import (
"sync"
"sync/atomic"
"unsafe"
)
@@ -30,18 +29,43 @@ func runtimeSemacquire(s *uint32)
//go:linkname runtimeSemrelease sync.runtime_Semrelease
func runtimeSemrelease(s *uint32, handoff bool, skipframes int)
// DowngradableRWMutex is identical to sync.RWMutex, but adds the DowngradeLock
// method.
// DowngradableRWMutex is identical to sync.RWMutex, but adds the DowngradeLock,
// TryLock and TryRLock methods.
type DowngradableRWMutex struct {
w sync.Mutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
w TMutex // held if there are pending writers
writerSem uint32 // semaphore for writers to wait for completing readers
readerSem uint32 // semaphore for readers to wait for completing writers
readerCount int32 // number of pending readers
readerWait int32 // number of departing readers
}
const rwmutexMaxReaders = 1 << 30
// TryRLock locks rw for reading. It returns true if it succeeds and false
// otherwise. It does not block.
func (rw *DowngradableRWMutex) TryRLock() bool {
if RaceEnabled {
RaceDisable()
}
for {
rc := atomic.LoadInt32(&rw.readerCount)
if rc < 0 {
if RaceEnabled {
RaceEnable()
}
return false
}
if !atomic.CompareAndSwapInt32(&rw.readerCount, rc, rc+1) {
continue
}
if RaceEnabled {
RaceEnable()
RaceAcquire(unsafe.Pointer(&rw.readerSem))
}
return true
}
}
// RLock locks rw for reading.
func (rw *DowngradableRWMutex) RLock() {
if RaceEnabled {
@@ -78,6 +102,34 @@ func (rw *DowngradableRWMutex) RUnlock() {
}
}
// TryLock locks rw for writing. It returns true if it succeeds and false
// otherwise. It does not block.
func (rw *DowngradableRWMutex) TryLock() bool {
if RaceEnabled {
RaceDisable()
}
// First, resolve competition with other writers.
if !rw.w.TryLock() {
if RaceEnabled {
RaceEnable()
}
return false
}
// Only proceed if there are no readers.
if !atomic.CompareAndSwapInt32(&rw.readerCount, 0, -rwmutexMaxReaders) {
rw.w.Unlock()
if RaceEnabled {
RaceEnable()
}
return false
}
if RaceEnabled {
RaceEnable()
RaceAcquire(unsafe.Pointer(&rw.writerSem))
}
return true
}
// Lock locks rw for writing.
func (rw *DowngradableRWMutex) Lock() {
if RaceEnabled {