blob: 5ca96d12b35e6ed4d3a658d35334871bafb9a5bf [file] [log] [blame]
// Copyright 2009 The Go Authors. All rights reserved.
// Copyright 2019 The gVisor Authors.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// GOMAXPROCS=10 go test
// Copy/pasted from the standard library's sync/rwmutex_test.go, except for the
// addition of downgradingWriter and the renaming of num_iterations to
// numIterations to shut up Golint.
package sync
import (
"fmt"
"runtime"
"sync/atomic"
"testing"
)
func parallelReader(m *RWMutex, clocked, cunlock, cdone chan bool) {
m.RLock()
clocked <- true
<-cunlock
m.RUnlock()
cdone <- true
}
func doTestParallelReaders(numReaders, gomaxprocs int) {
runtime.GOMAXPROCS(gomaxprocs)
var m RWMutex
clocked := make(chan bool)
cunlock := make(chan bool)
cdone := make(chan bool)
for i := 0; i < numReaders; i++ {
go parallelReader(&m, clocked, cunlock, cdone)
}
// Wait for all parallel RLock()s to succeed.
for i := 0; i < numReaders; i++ {
<-clocked
}
for i := 0; i < numReaders; i++ {
cunlock <- true
}
// Wait for the goroutines to finish.
for i := 0; i < numReaders; i++ {
<-cdone
}
}
func TestParallelReaders(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
doTestParallelReaders(1, 4)
doTestParallelReaders(3, 4)
doTestParallelReaders(4, 2)
}
func reader(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) {
for i := 0; i < numIterations; i++ {
rwm.RLock()
n := atomic.AddInt32(activity, 1)
if n < 1 || n >= 10000 {
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
atomic.AddInt32(activity, -1)
rwm.RUnlock()
}
cdone <- true
}
func writer(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) {
for i := 0; i < numIterations; i++ {
rwm.Lock()
n := atomic.AddInt32(activity, 10000)
if n != 10000 {
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
atomic.AddInt32(activity, -10000)
rwm.Unlock()
}
cdone <- true
}
func downgradingWriter(rwm *RWMutex, numIterations int, activity *int32, cdone chan bool) {
for i := 0; i < numIterations; i++ {
rwm.Lock()
n := atomic.AddInt32(activity, 10000)
if n != 10000 {
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
atomic.AddInt32(activity, -10000)
rwm.DowngradeLock()
n = atomic.AddInt32(activity, 1)
if n < 1 || n >= 10000 {
panic(fmt.Sprintf("wlock(%d)\n", n))
}
for i := 0; i < 100; i++ {
}
atomic.AddInt32(activity, -1)
rwm.RUnlock()
}
cdone <- true
}
func HammerDowngradableRWMutex(gomaxprocs, numReaders, numIterations int) {
runtime.GOMAXPROCS(gomaxprocs)
// Number of active readers + 10000 * number of active writers.
var activity int32
var rwm RWMutex
cdone := make(chan bool)
go writer(&rwm, numIterations, &activity, cdone)
go downgradingWriter(&rwm, numIterations, &activity, cdone)
var i int
for i = 0; i < numReaders/2; i++ {
go reader(&rwm, numIterations, &activity, cdone)
}
go writer(&rwm, numIterations, &activity, cdone)
go downgradingWriter(&rwm, numIterations, &activity, cdone)
for ; i < numReaders; i++ {
go reader(&rwm, numIterations, &activity, cdone)
}
// Wait for the 4 writers and all readers to finish.
for i := 0; i < 4+numReaders; i++ {
<-cdone
}
}
func TestDowngradableRWMutex(t *testing.T) {
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1))
n := 1000
if testing.Short() {
n = 5
}
HammerDowngradableRWMutex(1, 1, n)
HammerDowngradableRWMutex(1, 3, n)
HammerDowngradableRWMutex(1, 10, n)
HammerDowngradableRWMutex(4, 1, n)
HammerDowngradableRWMutex(4, 3, n)
HammerDowngradableRWMutex(4, 10, n)
HammerDowngradableRWMutex(10, 1, n)
HammerDowngradableRWMutex(10, 3, n)
HammerDowngradableRWMutex(10, 10, n)
HammerDowngradableRWMutex(10, 5, n)
}
func TestRWDoubleTryLock(t *testing.T) {
var rwm RWMutex
if !rwm.TryLock() {
t.Fatal("failed to aquire lock")
}
if rwm.TryLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestRWTryLockAfterLock(t *testing.T) {
var rwm RWMutex
rwm.Lock()
if rwm.TryLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestRWTryLockUnlock(t *testing.T) {
var rwm RWMutex
if !rwm.TryLock() {
t.Fatal("failed to aquire lock")
}
rwm.Unlock()
if !rwm.TryLock() {
t.Fatal("failed to aquire lock after unlock")
}
}
func TestTryRLockAfterLock(t *testing.T) {
var rwm RWMutex
rwm.Lock()
if rwm.TryRLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestTryLockAfterRLock(t *testing.T) {
var rwm RWMutex
rwm.RLock()
if rwm.TryLock() {
t.Fatal("unexpectedly succeeded in aquiring locked mutex")
}
}
func TestDoubleTryRLock(t *testing.T) {
var rwm RWMutex
if !rwm.TryRLock() {
t.Fatal("failed to aquire lock")
}
if !rwm.TryRLock() {
t.Fatal("failed to read aquire read locked lock")
}
}