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