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