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() | ||||
| } | ||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Nia Waldvogel
						Nia Waldvogel