mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-04 19:17:47 +00:00
206 lines
4.1 KiB
Go
206 lines
4.1 KiB
Go
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)
|
|
}
|
|
}
|