sync: add tests
Этот коммит содержится в:
родитель
8f2082df69
коммит
38305399a3
5 изменённых файлов: 386 добавлений и 0 удалений
1
Makefile
1
Makefile
|
@ -234,6 +234,7 @@ TEST_PACKAGES = \
|
|||
os \
|
||||
path \
|
||||
reflect \
|
||||
sync \
|
||||
testing \
|
||||
testing/iotest \
|
||||
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()
|
||||
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче