Source file src/runtime/proc_test.go

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

View as plain text