Black Lives Matter. Support the Equal Justice Initiative.

Source file src/runtime/proc_test.go

Documentation: runtime

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package runtime_test
     6  
     7  import (
     8  	"fmt"
     9  	"internal/race"
    10  	"internal/testenv"
    11  	"math"
    12  	"net"
    13  	"runtime"
    14  	"runtime/debug"
    15  	"strings"
    16  	"sync"
    17  	"sync/atomic"
    18  	"syscall"
    19  	"testing"
    20  	"time"
    21  )
    22  
    23  var stop = make(chan bool, 1)
    24  
    25  func perpetuumMobile() {
    26  	select {
    27  	case <-stop:
    28  	default:
    29  		go perpetuumMobile()
    30  	}
    31  }
    32  
    33  func TestStopTheWorldDeadlock(t *testing.T) {
    34  	if runtime.GOARCH == "wasm" {
    35  		t.Skip("no preemption on wasm yet")
    36  	}
    37  	if testing.Short() {
    38  		t.Skip("skipping during short test")
    39  	}
    40  	maxprocs := runtime.GOMAXPROCS(3)
    41  	compl := make(chan bool, 2)
    42  	go func() {
    43  		for i := 0; i != 1000; i += 1 {
    44  			runtime.GC()
    45  		}
    46  		compl <- true
    47  	}()
    48  	go func() {
    49  		for i := 0; i != 1000; i += 1 {
    50  			runtime.GOMAXPROCS(3)
    51  		}
    52  		compl <- true
    53  	}()
    54  	go perpetuumMobile()
    55  	<-compl
    56  	<-compl
    57  	stop <- true
    58  	runtime.GOMAXPROCS(maxprocs)
    59  }
    60  
    61  func TestYieldProgress(t *testing.T) {
    62  	testYieldProgress(false)
    63  }
    64  
    65  func TestYieldLockedProgress(t *testing.T) {
    66  	testYieldProgress(true)
    67  }
    68  
    69  func testYieldProgress(locked bool) {
    70  	c := make(chan bool)
    71  	cack := make(chan bool)
    72  	go func() {
    73  		if locked {
    74  			runtime.LockOSThread()
    75  		}
    76  		for {
    77  			select {
    78  			case <-c:
    79  				cack <- true
    80  				return
    81  			default:
    82  				runtime.Gosched()
    83  			}
    84  		}
    85  	}()
    86  	time.Sleep(10 * time.Millisecond)
    87  	c <- true
    88  	<-cack
    89  }
    90  
    91  func TestYieldLocked(t *testing.T) {
    92  	const N = 10
    93  	c := make(chan bool)
    94  	go func() {
    95  		runtime.LockOSThread()
    96  		for i := 0; i < N; i++ {
    97  			runtime.Gosched()
    98  			time.Sleep(time.Millisecond)
    99  		}
   100  		c <- true
   101  		// runtime.UnlockOSThread() is deliberately omitted
   102  	}()
   103  	<-c
   104  }
   105  
   106  func TestGoroutineParallelism(t *testing.T) {
   107  	if runtime.NumCPU() == 1 {
   108  		// Takes too long, too easy to deadlock, etc.
   109  		t.Skip("skipping on uniprocessor")
   110  	}
   111  	P := 4
   112  	N := 10
   113  	if testing.Short() {
   114  		P = 3
   115  		N = 3
   116  	}
   117  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
   118  	// If runtime triggers a forced GC during this test then it will deadlock,
   119  	// since the goroutines can't be stopped/preempted.
   120  	// Disable GC for this test (see issue #10958).
   121  	defer debug.SetGCPercent(debug.SetGCPercent(-1))
   122  	for try := 0; try < N; try++ {
   123  		done := make(chan bool)
   124  		x := uint32(0)
   125  		for p := 0; p < P; p++ {
   126  			// Test that all P goroutines are scheduled at the same time
   127  			go func(p int) {
   128  				for i := 0; i < 3; i++ {
   129  					expected := uint32(P*i + p)
   130  					for atomic.LoadUint32(&x) != expected {
   131  					}
   132  					atomic.StoreUint32(&x, expected+1)
   133  				}
   134  				done <- true
   135  			}(p)
   136  		}
   137  		for p := 0; p < P; p++ {
   138  			<-done
   139  		}
   140  	}
   141  }
   142  
   143  // Test that all runnable goroutines are scheduled at the same time.
   144  func TestGoroutineParallelism2(t *testing.T) {
   145  	//testGoroutineParallelism2(t, false, false)
   146  	testGoroutineParallelism2(t, true, false)
   147  	testGoroutineParallelism2(t, false, true)
   148  	testGoroutineParallelism2(t, true, true)
   149  }
   150  
   151  func testGoroutineParallelism2(t *testing.T, load, netpoll bool) {
   152  	if runtime.NumCPU() == 1 {
   153  		// Takes too long, too easy to deadlock, etc.
   154  		t.Skip("skipping on uniprocessor")
   155  	}
   156  	P := 4
   157  	N := 10
   158  	if testing.Short() {
   159  		N = 3
   160  	}
   161  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
   162  	// If runtime triggers a forced GC during this test then it will deadlock,
   163  	// since the goroutines can't be stopped/preempted.
   164  	// Disable GC for this test (see issue #10958).
   165  	defer debug.SetGCPercent(debug.SetGCPercent(-1))
   166  	for try := 0; try < N; try++ {
   167  		if load {
   168  			// Create P goroutines and wait until they all run.
   169  			// When we run the actual test below, worker threads
   170  			// running the goroutines will start parking.
   171  			done := make(chan bool)
   172  			x := uint32(0)
   173  			for p := 0; p < P; p++ {
   174  				go func() {
   175  					if atomic.AddUint32(&x, 1) == uint32(P) {
   176  						done <- true
   177  						return
   178  					}
   179  					for atomic.LoadUint32(&x) != uint32(P) {
   180  					}
   181  				}()
   182  			}
   183  			<-done
   184  		}
   185  		if netpoll {
   186  			// Enable netpoller, affects schedler behavior.
   187  			laddr := "localhost:0"
   188  			if runtime.GOOS == "android" {
   189  				// On some Android devices, there are no records for localhost,
   190  				// see https://golang.org/issues/14486.
   191  				// Don't use 127.0.0.1 for every case, it won't work on IPv6-only systems.
   192  				laddr = "127.0.0.1:0"
   193  			}
   194  			ln, err := net.Listen("tcp", laddr)
   195  			if err != nil {
   196  				defer ln.Close() // yup, defer in a loop
   197  			}
   198  		}
   199  		done := make(chan bool)
   200  		x := uint32(0)
   201  		// Spawn P goroutines in a nested fashion just to differ from TestGoroutineParallelism.
   202  		for p := 0; p < P/2; p++ {
   203  			go func(p int) {
   204  				for p2 := 0; p2 < 2; p2++ {
   205  					go func(p2 int) {
   206  						for i := 0; i < 3; i++ {
   207  							expected := uint32(P*i + p*2 + p2)
   208  							for atomic.LoadUint32(&x) != expected {
   209  							}
   210  							atomic.StoreUint32(&x, expected+1)
   211  						}
   212  						done <- true
   213  					}(p2)
   214  				}
   215  			}(p)
   216  		}
   217  		for p := 0; p < P; p++ {
   218  			<-done
   219  		}
   220  	}
   221  }
   222  
   223  func TestBlockLocked(t *testing.T) {
   224  	const N = 10
   225  	c := make(chan bool)
   226  	go func() {
   227  		runtime.LockOSThread()
   228  		for i := 0; i < N; i++ {
   229  			c <- true
   230  		}
   231  		runtime.UnlockOSThread()
   232  	}()
   233  	for i := 0; i < N; i++ {
   234  		<-c
   235  	}
   236  }
   237  
   238  func TestTimerFairness(t *testing.T) {
   239  	if runtime.GOARCH == "wasm" {
   240  		t.Skip("no preemption on wasm yet")
   241  	}
   242  
   243  	done := make(chan bool)
   244  	c := make(chan bool)
   245  	for i := 0; i < 2; i++ {
   246  		go func() {
   247  			for {
   248  				select {
   249  				case c <- true:
   250  				case <-done:
   251  					return
   252  				}
   253  			}
   254  		}()
   255  	}
   256  
   257  	timer := time.After(20 * time.Millisecond)
   258  	for {
   259  		select {
   260  		case <-c:
   261  		case <-timer:
   262  			close(done)
   263  			return
   264  		}
   265  	}
   266  }
   267  
   268  func TestTimerFairness2(t *testing.T) {
   269  	if runtime.GOARCH == "wasm" {
   270  		t.Skip("no preemption on wasm yet")
   271  	}
   272  
   273  	done := make(chan bool)
   274  	c := make(chan bool)
   275  	for i := 0; i < 2; i++ {
   276  		go func() {
   277  			timer := time.After(20 * time.Millisecond)
   278  			var buf [1]byte
   279  			for {
   280  				syscall.Read(0, buf[0:0])
   281  				select {
   282  				case c <- true:
   283  				case <-c:
   284  				case <-timer:
   285  					done <- true
   286  					return
   287  				}
   288  			}
   289  		}()
   290  	}
   291  	<-done
   292  	<-done
   293  }
   294  
   295  // The function is used to test preemption at split stack checks.
   296  // Declaring a var avoids inlining at the call site.
   297  var preempt = func() int {
   298  	var a [128]int
   299  	sum := 0
   300  	for _, v := range a {
   301  		sum += v
   302  	}
   303  	return sum
   304  }
   305  
   306  func TestPreemption(t *testing.T) {
   307  	if runtime.GOARCH == "wasm" {
   308  		t.Skip("no preemption on wasm yet")
   309  	}
   310  
   311  	// Test that goroutines are preempted at function calls.
   312  	N := 5
   313  	if testing.Short() {
   314  		N = 2
   315  	}
   316  	c := make(chan bool)
   317  	var x uint32
   318  	for g := 0; g < 2; g++ {
   319  		go func(g int) {
   320  			for i := 0; i < N; i++ {
   321  				for atomic.LoadUint32(&x) != uint32(g) {
   322  					preempt()
   323  				}
   324  				atomic.StoreUint32(&x, uint32(1-g))
   325  			}
   326  			c <- true
   327  		}(g)
   328  	}
   329  	<-c
   330  	<-c
   331  }
   332  
   333  func TestPreemptionGC(t *testing.T) {
   334  	if runtime.GOARCH == "wasm" {
   335  		t.Skip("no preemption on wasm yet")
   336  	}
   337  
   338  	// Test that pending GC preempts running goroutines.
   339  	P := 5
   340  	N := 10
   341  	if testing.Short() {
   342  		P = 3
   343  		N = 2
   344  	}
   345  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))
   346  	var stop uint32
   347  	for i := 0; i < P; i++ {
   348  		go func() {
   349  			for atomic.LoadUint32(&stop) == 0 {
   350  				preempt()
   351  			}
   352  		}()
   353  	}
   354  	for i := 0; i < N; i++ {
   355  		runtime.Gosched()
   356  		runtime.GC()
   357  	}
   358  	atomic.StoreUint32(&stop, 1)
   359  }
   360  
   361  func TestAsyncPreempt(t *testing.T) {
   362  	if !runtime.PreemptMSupported {
   363  		t.Skip("asynchronous preemption not supported on this platform")
   364  	}
   365  	output := runTestProg(t, "testprog", "AsyncPreempt")
   366  	want := "OK\n"
   367  	if output != want {
   368  		t.Fatalf("want %s, got %s\n", want, output)
   369  	}
   370  }
   371  
   372  func TestGCFairness(t *testing.T) {
   373  	output := runTestProg(t, "testprog", "GCFairness")
   374  	want := "OK\n"
   375  	if output != want {
   376  		t.Fatalf("want %s, got %s\n", want, output)
   377  	}
   378  }
   379  
   380  func TestGCFairness2(t *testing.T) {
   381  	output := runTestProg(t, "testprog", "GCFairness2")
   382  	want := "OK\n"
   383  	if output != want {
   384  		t.Fatalf("want %s, got %s\n", want, output)
   385  	}
   386  }
   387  
   388  func TestNumGoroutine(t *testing.T) {
   389  	output := runTestProg(t, "testprog", "NumGoroutine")
   390  	want := "1\n"
   391  	if output != want {
   392  		t.Fatalf("want %q, got %q", want, output)
   393  	}
   394  
   395  	buf := make([]byte, 1<<20)
   396  
   397  	// Try up to 10 times for a match before giving up.
   398  	// This is a fundamentally racy check but it's important
   399  	// to notice if NumGoroutine and Stack are _always_ out of sync.
   400  	for i := 0; ; i++ {
   401  		// Give goroutines about to exit a chance to exit.
   402  		// The NumGoroutine and Stack below need to see
   403  		// the same state of the world, so anything we can do
   404  		// to keep it quiet is good.
   405  		runtime.Gosched()
   406  
   407  		n := runtime.NumGoroutine()
   408  		buf = buf[:runtime.Stack(buf, true)]
   409  
   410  		nstk := strings.Count(string(buf), "goroutine ")
   411  		if n == nstk {
   412  			break
   413  		}
   414  		if i >= 10 {
   415  			t.Fatalf("NumGoroutine=%d, but found %d goroutines in stack dump: %s", n, nstk, buf)
   416  		}
   417  	}
   418  }
   419  
   420  func TestPingPongHog(t *testing.T) {
   421  	if runtime.GOARCH == "wasm" {
   422  		t.Skip("no preemption on wasm yet")
   423  	}
   424  	if testing.Short() {
   425  		t.Skip("skipping in -short mode")
   426  	}
   427  	if race.Enabled {
   428  		// The race detector randomizes the scheduler,
   429  		// which causes this test to fail (#38266).
   430  		t.Skip("skipping in -race mode")
   431  	}
   432  
   433  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
   434  	done := make(chan bool)
   435  	hogChan, lightChan := make(chan bool), make(chan bool)
   436  	hogCount, lightCount := 0, 0
   437  
   438  	run := func(limit int, counter *int, wake chan bool) {
   439  		for {
   440  			select {
   441  			case <-done:
   442  				return
   443  
   444  			case <-wake:
   445  				for i := 0; i < limit; i++ {
   446  					*counter++
   447  				}
   448  				wake <- true
   449  			}
   450  		}
   451  	}
   452  
   453  	// Start two co-scheduled hog goroutines.
   454  	for i := 0; i < 2; i++ {
   455  		go run(1e6, &hogCount, hogChan)
   456  	}
   457  
   458  	// Start two co-scheduled light goroutines.
   459  	for i := 0; i < 2; i++ {
   460  		go run(1e3, &lightCount, lightChan)
   461  	}
   462  
   463  	// Start goroutine pairs and wait for a few preemption rounds.
   464  	hogChan <- true
   465  	lightChan <- true
   466  	time.Sleep(100 * time.Millisecond)
   467  	close(done)
   468  	<-hogChan
   469  	<-lightChan
   470  
   471  	// Check that hogCount and lightCount are within a factor of
   472  	// 5, which indicates that both pairs of goroutines handed off
   473  	// the P within a time-slice to their buddy. We can use a
   474  	// fairly large factor here to make this robust: if the
   475  	// scheduler isn't working right, the gap should be ~1000X.
   476  	const factor = 5
   477  	if hogCount > lightCount*factor || lightCount > hogCount*factor {
   478  		t.Fatalf("want hogCount/lightCount in [%v, %v]; got %d/%d = %g", 1.0/factor, factor, hogCount, lightCount, float64(hogCount)/float64(lightCount))
   479  	}
   480  }
   481  
   482  func BenchmarkPingPongHog(b *testing.B) {
   483  	if b.N == 0 {
   484  		return
   485  	}
   486  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
   487  
   488  	// Create a CPU hog
   489  	stop, done := make(chan bool), make(chan bool)
   490  	go func() {
   491  		for {
   492  			select {
   493  			case <-stop:
   494  				done <- true
   495  				return
   496  			default:
   497  			}
   498  		}
   499  	}()
   500  
   501  	// Ping-pong b.N times
   502  	ping, pong := make(chan bool), make(chan bool)
   503  	go func() {
   504  		for j := 0; j < b.N; j++ {
   505  			pong <- <-ping
   506  		}
   507  		close(stop)
   508  		done <- true
   509  	}()
   510  	go func() {
   511  		for i := 0; i < b.N; i++ {
   512  			ping <- <-pong
   513  		}
   514  		done <- true
   515  	}()
   516  	b.ResetTimer()
   517  	ping <- true // Start ping-pong
   518  	<-stop
   519  	b.StopTimer()
   520  	<-ping // Let last ponger exit
   521  	<-done // Make sure goroutines exit
   522  	<-done
   523  	<-done
   524  }
   525  
   526  var padData [128]uint64
   527  
   528  func stackGrowthRecursive(i int) {
   529  	var pad [128]uint64
   530  	pad = padData
   531  	for j := range pad {
   532  		if pad[j] != 0 {
   533  			return
   534  		}
   535  	}
   536  	if i != 0 {
   537  		stackGrowthRecursive(i - 1)
   538  	}
   539  }
   540  
   541  func TestPreemptSplitBig(t *testing.T) {
   542  	if testing.Short() {
   543  		t.Skip("skipping in -short mode")
   544  	}
   545  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
   546  	stop := make(chan int)
   547  	go big(stop)
   548  	for i := 0; i < 3; i++ {
   549  		time.Sleep(10 * time.Microsecond) // let big start running
   550  		runtime.GC()
   551  	}
   552  	close(stop)
   553  }
   554  
   555  func big(stop chan int) int {
   556  	n := 0
   557  	for {
   558  		// delay so that gc is sure to have asked for a preemption
   559  		for i := 0; i < 1e9; i++ {
   560  			n++
   561  		}
   562  
   563  		// call bigframe, which used to miss the preemption in its prologue.
   564  		bigframe(stop)
   565  
   566  		// check if we've been asked to stop.
   567  		select {
   568  		case <-stop:
   569  			return n
   570  		}
   571  	}
   572  }
   573  
   574  func bigframe(stop chan int) int {
   575  	// not splitting the stack will overflow.
   576  	// small will notice that it needs a stack split and will
   577  	// catch the overflow.
   578  	var x [8192]byte
   579  	return small(stop, &x)
   580  }
   581  
   582  func small(stop chan int, x *[8192]byte) int {
   583  	for i := range x {
   584  		x[i] = byte(i)
   585  	}
   586  	sum := 0
   587  	for i := range x {
   588  		sum += int(x[i])
   589  	}
   590  
   591  	// keep small from being a leaf function, which might
   592  	// make it not do any stack check at all.
   593  	nonleaf(stop)
   594  
   595  	return sum
   596  }
   597  
   598  func nonleaf(stop chan int) bool {
   599  	// do something that won't be inlined:
   600  	select {
   601  	case <-stop:
   602  		return true
   603  	default:
   604  		return false
   605  	}
   606  }
   607  
   608  func TestSchedLocalQueue(t *testing.T) {
   609  	runtime.RunSchedLocalQueueTest()
   610  }
   611  
   612  func TestSchedLocalQueueSteal(t *testing.T) {
   613  	runtime.RunSchedLocalQueueStealTest()
   614  }
   615  
   616  func TestSchedLocalQueueEmpty(t *testing.T) {
   617  	if runtime.NumCPU() == 1 {
   618  		// Takes too long and does not trigger the race.
   619  		t.Skip("skipping on uniprocessor")
   620  	}
   621  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
   622  
   623  	// If runtime triggers a forced GC during this test then it will deadlock,
   624  	// since the goroutines can't be stopped/preempted during spin wait.
   625  	defer debug.SetGCPercent(debug.SetGCPercent(-1))
   626  
   627  	iters := int(1e5)
   628  	if testing.Short() {
   629  		iters = 1e2
   630  	}
   631  	runtime.RunSchedLocalQueueEmptyTest(iters)
   632  }
   633  
   634  func benchmarkStackGrowth(b *testing.B, rec int) {
   635  	b.RunParallel(func(pb *testing.PB) {
   636  		for pb.Next() {
   637  			stackGrowthRecursive(rec)
   638  		}
   639  	})
   640  }
   641  
   642  func BenchmarkStackGrowth(b *testing.B) {
   643  	benchmarkStackGrowth(b, 10)
   644  }
   645  
   646  func BenchmarkStackGrowthDeep(b *testing.B) {
   647  	benchmarkStackGrowth(b, 1024)
   648  }
   649  
   650  func BenchmarkCreateGoroutines(b *testing.B) {
   651  	benchmarkCreateGoroutines(b, 1)
   652  }
   653  
   654  func BenchmarkCreateGoroutinesParallel(b *testing.B) {
   655  	benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1))
   656  }
   657  
   658  func benchmarkCreateGoroutines(b *testing.B, procs int) {
   659  	c := make(chan bool)
   660  	var f func(n int)
   661  	f = func(n int) {
   662  		if n == 0 {
   663  			c <- true
   664  			return
   665  		}
   666  		go f(n - 1)
   667  	}
   668  	for i := 0; i < procs; i++ {
   669  		go f(b.N / procs)
   670  	}
   671  	for i := 0; i < procs; i++ {
   672  		<-c
   673  	}
   674  }
   675  
   676  func BenchmarkCreateGoroutinesCapture(b *testing.B) {
   677  	b.ReportAllocs()
   678  	for i := 0; i < b.N; i++ {
   679  		const N = 4
   680  		var wg sync.WaitGroup
   681  		wg.Add(N)
   682  		for i := 0; i < N; i++ {
   683  			i := i
   684  			go func() {
   685  				if i >= N {
   686  					b.Logf("bad") // just to capture b
   687  				}
   688  				wg.Done()
   689  			}()
   690  		}
   691  		wg.Wait()
   692  	}
   693  }
   694  
   695  // warmupScheduler ensures the scheduler has at least targetThreadCount threads
   696  // in its thread pool.
   697  func warmupScheduler(targetThreadCount int) {
   698  	var wg sync.WaitGroup
   699  	var count int32
   700  	for i := 0; i < targetThreadCount; i++ {
   701  		wg.Add(1)
   702  		go func() {
   703  			atomic.AddInt32(&count, 1)
   704  			for atomic.LoadInt32(&count) < int32(targetThreadCount) {
   705  				// spin until all threads started
   706  			}
   707  
   708  			// spin a bit more to ensure they are all running on separate CPUs.
   709  			doWork(time.Millisecond)
   710  			wg.Done()
   711  		}()
   712  	}
   713  	wg.Wait()
   714  }
   715  
   716  func doWork(dur time.Duration) {
   717  	start := time.Now()
   718  	for time.Since(start) < dur {
   719  	}
   720  }
   721  
   722  // BenchmarkCreateGoroutinesSingle creates many goroutines, all from a single
   723  // producer (the main benchmark goroutine).
   724  //
   725  // Compared to BenchmarkCreateGoroutines, this causes different behavior in the
   726  // scheduler because Ms are much more likely to need to steal work from the
   727  // main P rather than having work in the local run queue.
   728  func BenchmarkCreateGoroutinesSingle(b *testing.B) {
   729  	// Since we are interested in stealing behavior, warm the scheduler to
   730  	// get all the Ps running first.
   731  	warmupScheduler(runtime.GOMAXPROCS(0))
   732  	b.ResetTimer()
   733  
   734  	var wg sync.WaitGroup
   735  	wg.Add(b.N)
   736  	for i := 0; i < b.N; i++ {
   737  		go func() {
   738  			wg.Done()
   739  		}()
   740  	}
   741  	wg.Wait()
   742  }
   743  
   744  func BenchmarkClosureCall(b *testing.B) {
   745  	sum := 0
   746  	off1 := 1
   747  	for i := 0; i < b.N; i++ {
   748  		off2 := 2
   749  		func() {
   750  			sum += i + off1 + off2
   751  		}()
   752  	}
   753  	_ = sum
   754  }
   755  
   756  func benchmarkWakeupParallel(b *testing.B, spin func(time.Duration)) {
   757  	if runtime.GOMAXPROCS(0) == 1 {
   758  		b.Skip("skipping: GOMAXPROCS=1")
   759  	}
   760  
   761  	wakeDelay := 5 * time.Microsecond
   762  	for _, delay := range []time.Duration{
   763  		0,
   764  		1 * time.Microsecond,
   765  		2 * time.Microsecond,
   766  		5 * time.Microsecond,
   767  		10 * time.Microsecond,
   768  		20 * time.Microsecond,
   769  		50 * time.Microsecond,
   770  		100 * time.Microsecond,
   771  	} {
   772  		b.Run(delay.String(), func(b *testing.B) {
   773  			if b.N == 0 {
   774  				return
   775  			}
   776  			// Start two goroutines, which alternate between being
   777  			// sender and receiver in the following protocol:
   778  			//
   779  			// - The receiver spins for `delay` and then does a
   780  			// blocking receive on a channel.
   781  			//
   782  			// - The sender spins for `delay+wakeDelay` and then
   783  			// sends to the same channel. (The addition of
   784  			// `wakeDelay` improves the probability that the
   785  			// receiver will be blocking when the send occurs when
   786  			// the goroutines execute in parallel.)
   787  			//
   788  			// In each iteration of the benchmark, each goroutine
   789  			// acts once as sender and once as receiver, so each
   790  			// goroutine spins for delay twice.
   791  			//
   792  			// BenchmarkWakeupParallel is used to estimate how
   793  			// efficiently the scheduler parallelizes goroutines in
   794  			// the presence of blocking:
   795  			//
   796  			// - If both goroutines are executed on the same core,
   797  			// an increase in delay by N will increase the time per
   798  			// iteration by 4*N, because all 4 delays are
   799  			// serialized.
   800  			//
   801  			// - Otherwise, an increase in delay by N will increase
   802  			// the time per iteration by 2*N, and the time per
   803  			// iteration is 2 * (runtime overhead + chan
   804  			// send/receive pair + delay + wakeDelay). This allows
   805  			// the runtime overhead, including the time it takes
   806  			// for the unblocked goroutine to be scheduled, to be
   807  			// estimated.
   808  			ping, pong := make(chan struct{}), make(chan struct{})
   809  			start := make(chan struct{})
   810  			done := make(chan struct{})
   811  			go func() {
   812  				<-start
   813  				for i := 0; i < b.N; i++ {
   814  					// sender
   815  					spin(delay + wakeDelay)
   816  					ping <- struct{}{}
   817  					// receiver
   818  					spin(delay)
   819  					<-pong
   820  				}
   821  				done <- struct{}{}
   822  			}()
   823  			go func() {
   824  				for i := 0; i < b.N; i++ {
   825  					// receiver
   826  					spin(delay)
   827  					<-ping
   828  					// sender
   829  					spin(delay + wakeDelay)
   830  					pong <- struct{}{}
   831  				}
   832  				done <- struct{}{}
   833  			}()
   834  			b.ResetTimer()
   835  			start <- struct{}{}
   836  			<-done
   837  			<-done
   838  		})
   839  	}
   840  }
   841  
   842  func BenchmarkWakeupParallelSpinning(b *testing.B) {
   843  	benchmarkWakeupParallel(b, func(d time.Duration) {
   844  		end := time.Now().Add(d)
   845  		for time.Now().Before(end) {
   846  			// do nothing
   847  		}
   848  	})
   849  }
   850  
   851  // sysNanosleep is defined by OS-specific files (such as runtime_linux_test.go)
   852  // to sleep for the given duration. If nil, dependent tests are skipped.
   853  // The implementation should invoke a blocking system call and not
   854  // call time.Sleep, which would deschedule the goroutine.
   855  var sysNanosleep func(d time.Duration)
   856  
   857  func BenchmarkWakeupParallelSyscall(b *testing.B) {
   858  	if sysNanosleep == nil {
   859  		b.Skipf("skipping on %v; sysNanosleep not defined", runtime.GOOS)
   860  	}
   861  	benchmarkWakeupParallel(b, func(d time.Duration) {
   862  		sysNanosleep(d)
   863  	})
   864  }
   865  
   866  type Matrix [][]float64
   867  
   868  func BenchmarkMatmult(b *testing.B) {
   869  	b.StopTimer()
   870  	// matmult is O(N**3) but testing expects O(b.N),
   871  	// so we need to take cube root of b.N
   872  	n := int(math.Cbrt(float64(b.N))) + 1
   873  	A := makeMatrix(n)
   874  	B := makeMatrix(n)
   875  	C := makeMatrix(n)
   876  	b.StartTimer()
   877  	matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8)
   878  }
   879  
   880  func makeMatrix(n int) Matrix {
   881  	m := make(Matrix, n)
   882  	for i := 0; i < n; i++ {
   883  		m[i] = make([]float64, n)
   884  		for j := 0; j < n; j++ {
   885  			m[i][j] = float64(i*n + j)
   886  		}
   887  	}
   888  	return m
   889  }
   890  
   891  func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) {
   892  	di := i1 - i0
   893  	dj := j1 - j0
   894  	dk := k1 - k0
   895  	if di >= dj && di >= dk && di >= threshold {
   896  		// divide in two by y axis
   897  		mi := i0 + di/2
   898  		done1 := make(chan struct{}, 1)
   899  		go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold)
   900  		matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold)
   901  		<-done1
   902  	} else if dj >= dk && dj >= threshold {
   903  		// divide in two by x axis
   904  		mj := j0 + dj/2
   905  		done1 := make(chan struct{}, 1)
   906  		go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold)
   907  		matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold)
   908  		<-done1
   909  	} else if dk >= threshold {
   910  		// divide in two by "k" axis
   911  		// deliberately not parallel because of data races
   912  		mk := k0 + dk/2
   913  		matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold)
   914  		matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold)
   915  	} else {
   916  		// the matrices are small enough, compute directly
   917  		for i := i0; i < i1; i++ {
   918  			for j := j0; j < j1; j++ {
   919  				for k := k0; k < k1; k++ {
   920  					C[i][j] += A[i][k] * B[k][j]
   921  				}
   922  			}
   923  		}
   924  	}
   925  	if done != nil {
   926  		done <- struct{}{}
   927  	}
   928  }
   929  
   930  func TestStealOrder(t *testing.T) {
   931  	runtime.RunStealOrderTest()
   932  }
   933  
   934  func TestLockOSThreadNesting(t *testing.T) {
   935  	if runtime.GOARCH == "wasm" {
   936  		t.Skip("no threads on wasm yet")
   937  	}
   938  
   939  	go func() {
   940  		e, i := runtime.LockOSCounts()
   941  		if e != 0 || i != 0 {
   942  			t.Errorf("want locked counts 0, 0; got %d, %d", e, i)
   943  			return
   944  		}
   945  		runtime.LockOSThread()
   946  		runtime.LockOSThread()
   947  		runtime.UnlockOSThread()
   948  		e, i = runtime.LockOSCounts()
   949  		if e != 1 || i != 0 {
   950  			t.Errorf("want locked counts 1, 0; got %d, %d", e, i)
   951  			return
   952  		}
   953  		runtime.UnlockOSThread()
   954  		e, i = runtime.LockOSCounts()
   955  		if e != 0 || i != 0 {
   956  			t.Errorf("want locked counts 0, 0; got %d, %d", e, i)
   957  			return
   958  		}
   959  	}()
   960  }
   961  
   962  func TestLockOSThreadExit(t *testing.T) {
   963  	testLockOSThreadExit(t, "testprog")
   964  }
   965  
   966  func testLockOSThreadExit(t *testing.T, prog string) {
   967  	output := runTestProg(t, prog, "LockOSThreadMain", "GOMAXPROCS=1")
   968  	want := "OK\n"
   969  	if output != want {
   970  		t.Errorf("want %q, got %q", want, output)
   971  	}
   972  
   973  	output = runTestProg(t, prog, "LockOSThreadAlt")
   974  	if output != want {
   975  		t.Errorf("want %q, got %q", want, output)
   976  	}
   977  }
   978  
   979  func TestLockOSThreadAvoidsStatePropagation(t *testing.T) {
   980  	want := "OK\n"
   981  	skip := "unshare not permitted\n"
   982  	output := runTestProg(t, "testprog", "LockOSThreadAvoidsStatePropagation", "GOMAXPROCS=1")
   983  	if output == skip {
   984  		t.Skip("unshare syscall not permitted on this system")
   985  	} else if output != want {
   986  		t.Errorf("want %q, got %q", want, output)
   987  	}
   988  }
   989  
   990  func TestLockOSThreadTemplateThreadRace(t *testing.T) {
   991  	testenv.MustHaveGoRun(t)
   992  
   993  	exe, err := buildTestProg(t, "testprog")
   994  	if err != nil {
   995  		t.Fatal(err)
   996  	}
   997  
   998  	iterations := 100
   999  	if testing.Short() {
  1000  		// Reduce run time to ~100ms, with much lower probability of
  1001  		// catching issues.
  1002  		iterations = 5
  1003  	}
  1004  	for i := 0; i < iterations; i++ {
  1005  		want := "OK\n"
  1006  		output := runBuiltTestProg(t, exe, "LockOSThreadTemplateThreadRace")
  1007  		if output != want {
  1008  			t.Fatalf("run %d: want %q, got %q", i, want, output)
  1009  		}
  1010  	}
  1011  }
  1012  
  1013  // fakeSyscall emulates a system call.
  1014  //go:nosplit
  1015  func fakeSyscall(duration time.Duration) {
  1016  	runtime.Entersyscall()
  1017  	for start := runtime.Nanotime(); runtime.Nanotime()-start < int64(duration); {
  1018  	}
  1019  	runtime.Exitsyscall()
  1020  }
  1021  
  1022  // Check that a goroutine will be preempted if it is calling short system calls.
  1023  func testPreemptionAfterSyscall(t *testing.T, syscallDuration time.Duration) {
  1024  	if runtime.GOARCH == "wasm" {
  1025  		t.Skip("no preemption on wasm yet")
  1026  	}
  1027  
  1028  	defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
  1029  
  1030  	interations := 10
  1031  	if testing.Short() {
  1032  		interations = 1
  1033  	}
  1034  	const (
  1035  		maxDuration = 3 * time.Second
  1036  		nroutines   = 8
  1037  	)
  1038  
  1039  	for i := 0; i < interations; i++ {
  1040  		c := make(chan bool, nroutines)
  1041  		stop := uint32(0)
  1042  
  1043  		start := time.Now()
  1044  		for g := 0; g < nroutines; g++ {
  1045  			go func(stop *uint32) {
  1046  				c <- true
  1047  				for atomic.LoadUint32(stop) == 0 {
  1048  					fakeSyscall(syscallDuration)
  1049  				}
  1050  				c <- true
  1051  			}(&stop)
  1052  		}
  1053  		// wait until all goroutines have started.
  1054  		for g := 0; g < nroutines; g++ {
  1055  			<-c
  1056  		}
  1057  		atomic.StoreUint32(&stop, 1)
  1058  		// wait until all goroutines have finished.
  1059  		for g := 0; g < nroutines; g++ {
  1060  			<-c
  1061  		}
  1062  		duration := time.Since(start)
  1063  
  1064  		if duration > maxDuration {
  1065  			t.Errorf("timeout exceeded: %v (%v)", duration, maxDuration)
  1066  		}
  1067  	}
  1068  }
  1069  
  1070  func TestPreemptionAfterSyscall(t *testing.T) {
  1071  	for _, i := range []time.Duration{10, 100, 1000} {
  1072  		d := i * time.Microsecond
  1073  		t.Run(fmt.Sprint(d), func(t *testing.T) {
  1074  			testPreemptionAfterSyscall(t, d)
  1075  		})
  1076  	}
  1077  }
  1078  
  1079  func TestGetgThreadSwitch(t *testing.T) {
  1080  	runtime.RunGetgThreadSwitchTest()
  1081  }
  1082  
  1083  // TestNetpollBreak tests that netpollBreak can break a netpoll.
  1084  // This test is not particularly safe since the call to netpoll
  1085  // will pick up any stray files that are ready, but it should work
  1086  // OK as long it is not run in parallel.
  1087  func TestNetpollBreak(t *testing.T) {
  1088  	if runtime.GOMAXPROCS(0) == 1 {
  1089  		t.Skip("skipping: GOMAXPROCS=1")
  1090  	}
  1091  
  1092  	// Make sure that netpoll is initialized.
  1093  	runtime.NetpollGenericInit()
  1094  
  1095  	start := time.Now()
  1096  	c := make(chan bool, 2)
  1097  	go func() {
  1098  		c <- true
  1099  		runtime.Netpoll(10 * time.Second.Nanoseconds())
  1100  		c <- true
  1101  	}()
  1102  	<-c
  1103  	// Loop because the break might get eaten by the scheduler.
  1104  	// Break twice to break both the netpoll we started and the
  1105  	// scheduler netpoll.
  1106  loop:
  1107  	for {
  1108  		runtime.Usleep(100)
  1109  		runtime.NetpollBreak()
  1110  		runtime.NetpollBreak()
  1111  		select {
  1112  		case <-c:
  1113  			break loop
  1114  		default:
  1115  		}
  1116  	}
  1117  	if dur := time.Since(start); dur > 5*time.Second {
  1118  		t.Errorf("netpollBreak did not interrupt netpoll: slept for: %v", dur)
  1119  	}
  1120  }
  1121  
  1122  // TestBigGOMAXPROCS tests that setting GOMAXPROCS to a large value
  1123  // doesn't cause a crash at startup. See issue 38474.
  1124  func TestBigGOMAXPROCS(t *testing.T) {
  1125  	t.Parallel()
  1126  	output := runTestProg(t, "testprog", "NonexistentTest", "GOMAXPROCS=1024")
  1127  	// Ignore error conditions on small machines.
  1128  	for _, errstr := range []string{
  1129  		"failed to create new OS thread",
  1130  		"cannot allocate memory",
  1131  	} {
  1132  		if strings.Contains(output, errstr) {
  1133  			t.Skipf("failed to create 1024 threads")
  1134  		}
  1135  	}
  1136  	if !strings.Contains(output, "unknown function: NonexistentTest") {
  1137  		t.Errorf("output:\n%s\nwanted:\nunknown function: NonexistentTest", output)
  1138  	}
  1139  }
  1140  

View as plain text