sync: add tests
Этот коммит содержится в:
родитель
8f2082df69
коммит
38305399a3
5 изменённых файлов: 386 добавлений и 0 удалений
1
Makefile
1
Makefile
|
@ -234,6 +234,7 @@ TEST_PACKAGES = \
|
||||||
os \
|
os \
|
||||||
path \
|
path \
|
||||||
reflect \
|
reflect \
|
||||||
|
sync \
|
||||||
testing \
|
testing \
|
||||||
testing/iotest \
|
testing/iotest \
|
||||||
text/scanner \
|
text/scanner \
|
||||||
|
|
83
src/sync/cond_test.go
Обычный файл
83
src/sync/cond_test.go
Обычный файл
|
@ -0,0 +1,83 @@
|
||||||
|
package sync_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestCondSignal tests waiting on a Cond and notifying it with Signal.
|
||||||
|
func TestCondSignal(t *testing.T) {
|
||||||
|
// Create a Cond with a normal mutex.
|
||||||
|
cond := sync.Cond{
|
||||||
|
L: &sync.Mutex{},
|
||||||
|
}
|
||||||
|
cond.L.Lock()
|
||||||
|
|
||||||
|
// Start a goroutine to signal us once we wait.
|
||||||
|
var signaled uint32
|
||||||
|
go func() {
|
||||||
|
// Wait for the test goroutine to wait.
|
||||||
|
cond.L.Lock()
|
||||||
|
defer cond.L.Unlock()
|
||||||
|
|
||||||
|
// Send a signal to the test goroutine.
|
||||||
|
atomic.StoreUint32(&signaled, 1)
|
||||||
|
cond.Signal()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for a signal.
|
||||||
|
// This will unlock the mutex, and allow the spawned goroutine to run.
|
||||||
|
cond.Wait()
|
||||||
|
if atomic.LoadUint32(&signaled) == 0 {
|
||||||
|
t.Error("wait returned before a signal was sent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCondBroadcast(t *testing.T) {
|
||||||
|
// Create a Cond with an RWMutex.
|
||||||
|
var mu sync.RWMutex
|
||||||
|
cond := sync.Cond{
|
||||||
|
L: mu.RLocker(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start goroutines to wait for the broadcast.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
const n = 5
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
mu.RLock()
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
cond.Wait()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for all goroutines to start waiting.
|
||||||
|
mu.Lock()
|
||||||
|
|
||||||
|
// Broadcast to all of the waiting goroutines.
|
||||||
|
cond.Broadcast()
|
||||||
|
|
||||||
|
// Wait for all spawned goroutines to process the broadcast.
|
||||||
|
mu.Unlock()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCondUnlockNotify verifies that a signal is processed even if it happens during the mutex unlock in Wait.
|
||||||
|
func TestCondUnlockNotify(t *testing.T) {
|
||||||
|
// Create a Cond that signals itself when waiting.
|
||||||
|
var cond sync.Cond
|
||||||
|
cond.L = fakeLocker{cond.Signal}
|
||||||
|
|
||||||
|
cond.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// fakeLocker is a fake sync.Locker where unlock calls an arbitrary function.
|
||||||
|
type fakeLocker struct {
|
||||||
|
unlock func()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l fakeLocker) Lock() {}
|
||||||
|
func (l fakeLocker) Unlock() { l.unlock() }
|
205
src/sync/mutex_test.go
Обычный файл
205
src/sync/mutex_test.go
Обычный файл
|
@ -0,0 +1,205 @@
|
||||||
|
package sync_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestMutexUncontended tests locking and unlocking a Mutex that is not shared with any other goroutines.
|
||||||
|
func TestMutexUncontended(t *testing.T) {
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
// Lock and unlock the mutex a few times.
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
mu.Lock()
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestMutexConcurrent tests a mutex concurrently from multiple goroutines.
|
||||||
|
// It will fail if multiple goroutines hold the lock simultaneously.
|
||||||
|
func TestMutexConcurrent(t *testing.T) {
|
||||||
|
var mu sync.Mutex
|
||||||
|
var active uint
|
||||||
|
var completed uint
|
||||||
|
ok := true
|
||||||
|
|
||||||
|
const n = 10
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
j := i
|
||||||
|
go func() {
|
||||||
|
// Delay a bit.
|
||||||
|
for k := j; k > 0; k-- {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
|
||||||
|
mu.Lock()
|
||||||
|
|
||||||
|
// Increment the active counter.
|
||||||
|
active++
|
||||||
|
|
||||||
|
if active > 1 {
|
||||||
|
// Multiple things are holding the lock at the same time.
|
||||||
|
ok = false
|
||||||
|
} else {
|
||||||
|
// Delay a bit.
|
||||||
|
for k := j; k < n; k++ {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrement the active counter.
|
||||||
|
active--
|
||||||
|
|
||||||
|
// This is completed.
|
||||||
|
completed++
|
||||||
|
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for everything to finish.
|
||||||
|
var done bool
|
||||||
|
for !done {
|
||||||
|
// Wait a bit for other things to run.
|
||||||
|
runtime.Gosched()
|
||||||
|
|
||||||
|
// Acquire the lock and check whether everything has completed.
|
||||||
|
mu.Lock()
|
||||||
|
done = completed == n
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
t.Error("lock held concurrently")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRWMutexUncontended tests locking and unlocking an RWMutex that is not shared with any other goroutines.
|
||||||
|
func TestRWMutexUncontended(t *testing.T) {
|
||||||
|
var mu sync.RWMutex
|
||||||
|
|
||||||
|
// Lock the mutex exclusively and then unlock it.
|
||||||
|
mu.Lock()
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// Acuire several read locks.
|
||||||
|
const n = 5
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mu.RLock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release all of the read locks.
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mu.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-acquire the lock exclusively.
|
||||||
|
mu.Lock()
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRWMutexWriteToRead tests the transition from a write lock to a read lock while contended.
|
||||||
|
func TestRWMutexWriteToRead(t *testing.T) {
|
||||||
|
// Create a new RWMutex and acquire a write lock.
|
||||||
|
var mu sync.RWMutex
|
||||||
|
mu.Lock()
|
||||||
|
|
||||||
|
const n = 3
|
||||||
|
var readAcquires uint32
|
||||||
|
var completed uint32
|
||||||
|
var unlocked uint32
|
||||||
|
var bad uint32
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
go func() {
|
||||||
|
// Acquire a read lock.
|
||||||
|
mu.RLock()
|
||||||
|
|
||||||
|
// Verify that the write lock is supposed to be released by now.
|
||||||
|
if atomic.LoadUint32(&unlocked) == 0 {
|
||||||
|
// The write lock is still being held.
|
||||||
|
atomic.AddUint32(&bad, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add ourselves to the read lock counter.
|
||||||
|
atomic.AddUint32(&readAcquires, 1)
|
||||||
|
|
||||||
|
// Wait for everything to hold the read lock simultaneously.
|
||||||
|
for atomic.LoadUint32(&readAcquires) < n {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify of completion.
|
||||||
|
atomic.AddUint32(&completed, 1)
|
||||||
|
|
||||||
|
// Release the read lock.
|
||||||
|
mu.RUnlock()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait a bit for the goroutines to block.
|
||||||
|
for i := 0; i < 3*n; i++ {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release the write lock so that the goroutines acquire read locks.
|
||||||
|
atomic.StoreUint32(&unlocked, 1)
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// Wait for everything to complete.
|
||||||
|
for atomic.LoadUint32(&completed) < n {
|
||||||
|
runtime.Gosched()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Acquire another write lock.
|
||||||
|
mu.Lock()
|
||||||
|
|
||||||
|
if bad != 0 {
|
||||||
|
t.Error("read lock acquired while write-locked")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestRWMutexWriteToRead tests the transition from a read lock to a write lock while contended.
|
||||||
|
func TestRWMutexReadToWrite(t *testing.T) {
|
||||||
|
// Create a new RWMutex and read-lock it several times.
|
||||||
|
const n = 3
|
||||||
|
var mu sync.RWMutex
|
||||||
|
var readers uint32
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
mu.RLock()
|
||||||
|
readers++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a goroutine to acquire a write lock.
|
||||||
|
result := ^uint32(0)
|
||||||
|
go func() {
|
||||||
|
// Acquire a write lock.
|
||||||
|
mu.Lock()
|
||||||
|
|
||||||
|
// Check for active readers.
|
||||||
|
readers := atomic.LoadUint32(&readers)
|
||||||
|
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
// Report the number of active readers.
|
||||||
|
atomic.StoreUint32(&result, readers)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Release the read locks.
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
runtime.Gosched()
|
||||||
|
atomic.AddUint32(&readers, ^uint32(0))
|
||||||
|
mu.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for a result.
|
||||||
|
var res uint32
|
||||||
|
for res == ^uint32(0) {
|
||||||
|
runtime.Gosched()
|
||||||
|
res = atomic.LoadUint32(&result)
|
||||||
|
}
|
||||||
|
if res != 0 {
|
||||||
|
t.Errorf("write lock acquired while %d readers were active", res)
|
||||||
|
}
|
||||||
|
}
|
62
src/sync/once_test.go
Обычный файл
62
src/sync/once_test.go
Обычный файл
|
@ -0,0 +1,62 @@
|
||||||
|
package sync_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestOnceUncontended tests Once on a single goroutine.
|
||||||
|
func TestOnceUncontended(t *testing.T) {
|
||||||
|
var once sync.Once
|
||||||
|
{
|
||||||
|
var ran bool
|
||||||
|
once.Do(func() {
|
||||||
|
ran = true
|
||||||
|
})
|
||||||
|
if !ran {
|
||||||
|
t.Error("first call to Do did not run")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var ran bool
|
||||||
|
once.Do(func() {
|
||||||
|
ran = true
|
||||||
|
})
|
||||||
|
if ran {
|
||||||
|
t.Error("second call to Do ran")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestOnceConcurrent tests multiple concurrent invocations of sync.Once.
|
||||||
|
func TestOnceConcurrent(t *testing.T) {
|
||||||
|
var once sync.Once
|
||||||
|
var mu sync.Mutex
|
||||||
|
mu.Lock()
|
||||||
|
var ran bool
|
||||||
|
var ranTwice bool
|
||||||
|
once.Do(func() {
|
||||||
|
ran = true
|
||||||
|
|
||||||
|
// Start a goroutine and (approximately) wait for it to enter the call to Do.
|
||||||
|
var startWait sync.Mutex
|
||||||
|
startWait.Lock()
|
||||||
|
go func() {
|
||||||
|
startWait.Unlock()
|
||||||
|
once.Do(func() {
|
||||||
|
ranTwice = true
|
||||||
|
})
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
startWait.Lock()
|
||||||
|
})
|
||||||
|
if !ran {
|
||||||
|
t.Error("first call to Do did not run")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the goroutine to finish.
|
||||||
|
mu.Lock()
|
||||||
|
if ranTwice {
|
||||||
|
t.Error("second concurrent call to Once also ran")
|
||||||
|
}
|
||||||
|
}
|
35
src/sync/waitgroup_test.go
Обычный файл
35
src/sync/waitgroup_test.go
Обычный файл
|
@ -0,0 +1,35 @@
|
||||||
|
package sync_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestWaitGroupUncontended tests the wait group from a single goroutine.
|
||||||
|
func TestWaitGroupUncontended(t *testing.T) {
|
||||||
|
// Check that a single add-and-done works.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
wg.Done()
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Check that mixing positive and negative counts works.
|
||||||
|
wg.Add(10)
|
||||||
|
wg.Add(-8)
|
||||||
|
wg.Add(-1)
|
||||||
|
wg.Add(0)
|
||||||
|
wg.Done()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestWaitGroup tests the typical usage of WaitGroup.
|
||||||
|
func TestWaitGroup(t *testing.T) {
|
||||||
|
const n = 5
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
go wg.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче