github.com/likebike/go--@v0.0.0-20190911215757-0bd925d16e96/go/src/runtime/proc_test.go (about) 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 "math" 9 "net" 10 "runtime" 11 "runtime/debug" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "syscall" 16 "testing" 17 "time" 18 ) 19 20 var stop = make(chan bool, 1) 21 22 func perpetuumMobile() { 23 select { 24 case <-stop: 25 default: 26 go perpetuumMobile() 27 } 28 } 29 30 func TestStopTheWorldDeadlock(t *testing.T) { 31 if testing.Short() { 32 t.Skip("skipping during short test") 33 } 34 maxprocs := runtime.GOMAXPROCS(3) 35 compl := make(chan bool, 2) 36 go func() { 37 for i := 0; i != 1000; i += 1 { 38 runtime.GC() 39 } 40 compl <- true 41 }() 42 go func() { 43 for i := 0; i != 1000; i += 1 { 44 runtime.GOMAXPROCS(3) 45 } 46 compl <- true 47 }() 48 go perpetuumMobile() 49 <-compl 50 <-compl 51 stop <- true 52 runtime.GOMAXPROCS(maxprocs) 53 } 54 55 func TestYieldProgress(t *testing.T) { 56 testYieldProgress(false) 57 } 58 59 func TestYieldLockedProgress(t *testing.T) { 60 testYieldProgress(true) 61 } 62 63 func testYieldProgress(locked bool) { 64 c := make(chan bool) 65 cack := make(chan bool) 66 go func() { 67 if locked { 68 runtime.LockOSThread() 69 } 70 for { 71 select { 72 case <-c: 73 cack <- true 74 return 75 default: 76 runtime.Gosched() 77 } 78 } 79 }() 80 time.Sleep(10 * time.Millisecond) 81 c <- true 82 <-cack 83 } 84 85 func TestYieldLocked(t *testing.T) { 86 const N = 10 87 c := make(chan bool) 88 go func() { 89 runtime.LockOSThread() 90 for i := 0; i < N; i++ { 91 runtime.Gosched() 92 time.Sleep(time.Millisecond) 93 } 94 c <- true 95 // runtime.UnlockOSThread() is deliberately omitted 96 }() 97 <-c 98 } 99 100 func TestGoroutineParallelism(t *testing.T) { 101 if runtime.NumCPU() == 1 { 102 // Takes too long, too easy to deadlock, etc. 103 t.Skip("skipping on uniprocessor") 104 } 105 P := 4 106 N := 10 107 if testing.Short() { 108 P = 3 109 N = 3 110 } 111 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) 112 // If runtime triggers a forced GC during this test then it will deadlock, 113 // since the goroutines can't be stopped/preempted. 114 // Disable GC for this test (see issue #10958). 115 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 116 for try := 0; try < N; try++ { 117 done := make(chan bool) 118 x := uint32(0) 119 for p := 0; p < P; p++ { 120 // Test that all P goroutines are scheduled at the same time 121 go func(p int) { 122 for i := 0; i < 3; i++ { 123 expected := uint32(P*i + p) 124 for atomic.LoadUint32(&x) != expected { 125 } 126 atomic.StoreUint32(&x, expected+1) 127 } 128 done <- true 129 }(p) 130 } 131 for p := 0; p < P; p++ { 132 <-done 133 } 134 } 135 } 136 137 // Test that all runnable goroutines are scheduled at the same time. 138 func TestGoroutineParallelism2(t *testing.T) { 139 //testGoroutineParallelism2(t, false, false) 140 testGoroutineParallelism2(t, true, false) 141 testGoroutineParallelism2(t, false, true) 142 testGoroutineParallelism2(t, true, true) 143 } 144 145 func testGoroutineParallelism2(t *testing.T, load, netpoll bool) { 146 if runtime.NumCPU() == 1 { 147 // Takes too long, too easy to deadlock, etc. 148 t.Skip("skipping on uniprocessor") 149 } 150 P := 4 151 N := 10 152 if testing.Short() { 153 N = 3 154 } 155 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) 156 // If runtime triggers a forced GC during this test then it will deadlock, 157 // since the goroutines can't be stopped/preempted. 158 // Disable GC for this test (see issue #10958). 159 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 160 for try := 0; try < N; try++ { 161 if load { 162 // Create P goroutines and wait until they all run. 163 // When we run the actual test below, worker threads 164 // running the goroutines will start parking. 165 done := make(chan bool) 166 x := uint32(0) 167 for p := 0; p < P; p++ { 168 go func() { 169 if atomic.AddUint32(&x, 1) == uint32(P) { 170 done <- true 171 return 172 } 173 for atomic.LoadUint32(&x) != uint32(P) { 174 } 175 }() 176 } 177 <-done 178 } 179 if netpoll { 180 // Enable netpoller, affects schedler behavior. 181 laddr := "localhost:0" 182 if runtime.GOOS == "android" { 183 // On some Android devices, there are no records for localhost, 184 // see https://golang.org/issues/14486. 185 // Don't use 127.0.0.1 for every case, it won't work on IPv6-only systems. 186 laddr = "127.0.0.1:0" 187 } 188 ln, err := net.Listen("tcp", laddr) 189 if err != nil { 190 defer ln.Close() // yup, defer in a loop 191 } 192 } 193 done := make(chan bool) 194 x := uint32(0) 195 // Spawn P goroutines in a nested fashion just to differ from TestGoroutineParallelism. 196 for p := 0; p < P/2; p++ { 197 go func(p int) { 198 for p2 := 0; p2 < 2; p2++ { 199 go func(p2 int) { 200 for i := 0; i < 3; i++ { 201 expected := uint32(P*i + p*2 + p2) 202 for atomic.LoadUint32(&x) != expected { 203 } 204 atomic.StoreUint32(&x, expected+1) 205 } 206 done <- true 207 }(p2) 208 } 209 }(p) 210 } 211 for p := 0; p < P; p++ { 212 <-done 213 } 214 } 215 } 216 217 func TestBlockLocked(t *testing.T) { 218 const N = 10 219 c := make(chan bool) 220 go func() { 221 runtime.LockOSThread() 222 for i := 0; i < N; i++ { 223 c <- true 224 } 225 runtime.UnlockOSThread() 226 }() 227 for i := 0; i < N; i++ { 228 <-c 229 } 230 } 231 232 func TestTimerFairness(t *testing.T) { 233 done := make(chan bool) 234 c := make(chan bool) 235 for i := 0; i < 2; i++ { 236 go func() { 237 for { 238 select { 239 case c <- true: 240 case <-done: 241 return 242 } 243 } 244 }() 245 } 246 247 timer := time.After(20 * time.Millisecond) 248 for { 249 select { 250 case <-c: 251 case <-timer: 252 close(done) 253 return 254 } 255 } 256 } 257 258 func TestTimerFairness2(t *testing.T) { 259 done := make(chan bool) 260 c := make(chan bool) 261 for i := 0; i < 2; i++ { 262 go func() { 263 timer := time.After(20 * time.Millisecond) 264 var buf [1]byte 265 for { 266 syscall.Read(0, buf[0:0]) 267 select { 268 case c <- true: 269 case <-c: 270 case <-timer: 271 done <- true 272 return 273 } 274 } 275 }() 276 } 277 <-done 278 <-done 279 } 280 281 // The function is used to test preemption at split stack checks. 282 // Declaring a var avoids inlining at the call site. 283 var preempt = func() int { 284 var a [128]int 285 sum := 0 286 for _, v := range a { 287 sum += v 288 } 289 return sum 290 } 291 292 func TestPreemption(t *testing.T) { 293 // Test that goroutines are preempted at function calls. 294 N := 5 295 if testing.Short() { 296 N = 2 297 } 298 c := make(chan bool) 299 var x uint32 300 for g := 0; g < 2; g++ { 301 go func(g int) { 302 for i := 0; i < N; i++ { 303 for atomic.LoadUint32(&x) != uint32(g) { 304 preempt() 305 } 306 atomic.StoreUint32(&x, uint32(1-g)) 307 } 308 c <- true 309 }(g) 310 } 311 <-c 312 <-c 313 } 314 315 func TestPreemptionGC(t *testing.T) { 316 // Test that pending GC preempts running goroutines. 317 P := 5 318 N := 10 319 if testing.Short() { 320 P = 3 321 N = 2 322 } 323 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1)) 324 var stop uint32 325 for i := 0; i < P; i++ { 326 go func() { 327 for atomic.LoadUint32(&stop) == 0 { 328 preempt() 329 } 330 }() 331 } 332 for i := 0; i < N; i++ { 333 runtime.Gosched() 334 runtime.GC() 335 } 336 atomic.StoreUint32(&stop, 1) 337 } 338 339 func TestGCFairness(t *testing.T) { 340 output := runTestProg(t, "testprog", "GCFairness") 341 want := "OK\n" 342 if output != want { 343 t.Fatalf("want %s, got %s\n", want, output) 344 } 345 } 346 347 func TestGCFairness2(t *testing.T) { 348 output := runTestProg(t, "testprog", "GCFairness2") 349 want := "OK\n" 350 if output != want { 351 t.Fatalf("want %s, got %s\n", want, output) 352 } 353 } 354 355 func TestNumGoroutine(t *testing.T) { 356 output := runTestProg(t, "testprog", "NumGoroutine") 357 want := "1\n" 358 if output != want { 359 t.Fatalf("want %q, got %q", want, output) 360 } 361 362 buf := make([]byte, 1<<20) 363 364 // Try up to 10 times for a match before giving up. 365 // This is a fundamentally racy check but it's important 366 // to notice if NumGoroutine and Stack are _always_ out of sync. 367 for i := 0; ; i++ { 368 // Give goroutines about to exit a chance to exit. 369 // The NumGoroutine and Stack below need to see 370 // the same state of the world, so anything we can do 371 // to keep it quiet is good. 372 runtime.Gosched() 373 374 n := runtime.NumGoroutine() 375 buf = buf[:runtime.Stack(buf, true)] 376 377 nstk := strings.Count(string(buf), "goroutine ") 378 if n == nstk { 379 break 380 } 381 if i >= 10 { 382 t.Fatalf("NumGoroutine=%d, but found %d goroutines in stack dump: %s", n, nstk, buf) 383 } 384 } 385 } 386 387 func TestPingPongHog(t *testing.T) { 388 if testing.Short() { 389 t.Skip("skipping in -short mode") 390 } 391 392 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 393 done := make(chan bool) 394 hogChan, lightChan := make(chan bool), make(chan bool) 395 hogCount, lightCount := 0, 0 396 397 run := func(limit int, counter *int, wake chan bool) { 398 for { 399 select { 400 case <-done: 401 return 402 403 case <-wake: 404 for i := 0; i < limit; i++ { 405 *counter++ 406 } 407 wake <- true 408 } 409 } 410 } 411 412 // Start two co-scheduled hog goroutines. 413 for i := 0; i < 2; i++ { 414 go run(1e6, &hogCount, hogChan) 415 } 416 417 // Start two co-scheduled light goroutines. 418 for i := 0; i < 2; i++ { 419 go run(1e3, &lightCount, lightChan) 420 } 421 422 // Start goroutine pairs and wait for a few preemption rounds. 423 hogChan <- true 424 lightChan <- true 425 time.Sleep(100 * time.Millisecond) 426 close(done) 427 <-hogChan 428 <-lightChan 429 430 // Check that hogCount and lightCount are within a factor of 431 // 5, which indicates that both pairs of goroutines handed off 432 // the P within a time-slice to their buddy. We can use a 433 // fairly large factor here to make this robust: if the 434 // scheduler isn't working right, the gap should be ~1000X. 435 const factor = 5 436 if hogCount > lightCount*factor || lightCount > hogCount*factor { 437 t.Fatalf("want hogCount/lightCount in [%v, %v]; got %d/%d = %g", 1.0/factor, factor, hogCount, lightCount, float64(hogCount)/float64(lightCount)) 438 } 439 } 440 441 func BenchmarkPingPongHog(b *testing.B) { 442 if b.N == 0 { 443 return 444 } 445 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) 446 447 // Create a CPU hog 448 stop, done := make(chan bool), make(chan bool) 449 go func() { 450 for { 451 select { 452 case <-stop: 453 done <- true 454 return 455 default: 456 } 457 } 458 }() 459 460 // Ping-pong b.N times 461 ping, pong := make(chan bool), make(chan bool) 462 go func() { 463 for j := 0; j < b.N; j++ { 464 pong <- <-ping 465 } 466 close(stop) 467 done <- true 468 }() 469 go func() { 470 for i := 0; i < b.N; i++ { 471 ping <- <-pong 472 } 473 done <- true 474 }() 475 b.ResetTimer() 476 ping <- true // Start ping-pong 477 <-stop 478 b.StopTimer() 479 <-ping // Let last ponger exit 480 <-done // Make sure goroutines exit 481 <-done 482 <-done 483 } 484 485 func stackGrowthRecursive(i int) { 486 var pad [128]uint64 487 if i != 0 && pad[0] == 0 { 488 stackGrowthRecursive(i - 1) 489 } 490 } 491 492 func TestPreemptSplitBig(t *testing.T) { 493 if testing.Short() { 494 t.Skip("skipping in -short mode") 495 } 496 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2)) 497 stop := make(chan int) 498 go big(stop) 499 for i := 0; i < 3; i++ { 500 time.Sleep(10 * time.Microsecond) // let big start running 501 runtime.GC() 502 } 503 close(stop) 504 } 505 506 func big(stop chan int) int { 507 n := 0 508 for { 509 // delay so that gc is sure to have asked for a preemption 510 for i := 0; i < 1e9; i++ { 511 n++ 512 } 513 514 // call bigframe, which used to miss the preemption in its prologue. 515 bigframe(stop) 516 517 // check if we've been asked to stop. 518 select { 519 case <-stop: 520 return n 521 } 522 } 523 } 524 525 func bigframe(stop chan int) int { 526 // not splitting the stack will overflow. 527 // small will notice that it needs a stack split and will 528 // catch the overflow. 529 var x [8192]byte 530 return small(stop, &x) 531 } 532 533 func small(stop chan int, x *[8192]byte) int { 534 for i := range x { 535 x[i] = byte(i) 536 } 537 sum := 0 538 for i := range x { 539 sum += int(x[i]) 540 } 541 542 // keep small from being a leaf function, which might 543 // make it not do any stack check at all. 544 nonleaf(stop) 545 546 return sum 547 } 548 549 func nonleaf(stop chan int) bool { 550 // do something that won't be inlined: 551 select { 552 case <-stop: 553 return true 554 default: 555 return false 556 } 557 } 558 559 func TestSchedLocalQueue(t *testing.T) { 560 runtime.RunSchedLocalQueueTest() 561 } 562 563 func TestSchedLocalQueueSteal(t *testing.T) { 564 runtime.RunSchedLocalQueueStealTest() 565 } 566 567 func TestSchedLocalQueueEmpty(t *testing.T) { 568 if runtime.NumCPU() == 1 { 569 // Takes too long and does not trigger the race. 570 t.Skip("skipping on uniprocessor") 571 } 572 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4)) 573 574 // If runtime triggers a forced GC during this test then it will deadlock, 575 // since the goroutines can't be stopped/preempted during spin wait. 576 defer debug.SetGCPercent(debug.SetGCPercent(-1)) 577 578 iters := int(1e5) 579 if testing.Short() { 580 iters = 1e2 581 } 582 runtime.RunSchedLocalQueueEmptyTest(iters) 583 } 584 585 func benchmarkStackGrowth(b *testing.B, rec int) { 586 b.RunParallel(func(pb *testing.PB) { 587 for pb.Next() { 588 stackGrowthRecursive(rec) 589 } 590 }) 591 } 592 593 func BenchmarkStackGrowth(b *testing.B) { 594 benchmarkStackGrowth(b, 10) 595 } 596 597 func BenchmarkStackGrowthDeep(b *testing.B) { 598 benchmarkStackGrowth(b, 1024) 599 } 600 601 func BenchmarkCreateGoroutines(b *testing.B) { 602 benchmarkCreateGoroutines(b, 1) 603 } 604 605 func BenchmarkCreateGoroutinesParallel(b *testing.B) { 606 benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1)) 607 } 608 609 func benchmarkCreateGoroutines(b *testing.B, procs int) { 610 c := make(chan bool) 611 var f func(n int) 612 f = func(n int) { 613 if n == 0 { 614 c <- true 615 return 616 } 617 go f(n - 1) 618 } 619 for i := 0; i < procs; i++ { 620 go f(b.N / procs) 621 } 622 for i := 0; i < procs; i++ { 623 <-c 624 } 625 } 626 627 func BenchmarkCreateGoroutinesCapture(b *testing.B) { 628 b.ReportAllocs() 629 for i := 0; i < b.N; i++ { 630 const N = 4 631 var wg sync.WaitGroup 632 wg.Add(N) 633 for i := 0; i < N; i++ { 634 i := i 635 go func() { 636 if i >= N { 637 b.Logf("bad") // just to capture b 638 } 639 wg.Done() 640 }() 641 } 642 wg.Wait() 643 } 644 } 645 646 func BenchmarkClosureCall(b *testing.B) { 647 sum := 0 648 off1 := 1 649 for i := 0; i < b.N; i++ { 650 off2 := 2 651 func() { 652 sum += i + off1 + off2 653 }() 654 } 655 _ = sum 656 } 657 658 func benchmarkWakeupParallel(b *testing.B, spin func(time.Duration)) { 659 if runtime.GOMAXPROCS(0) == 1 { 660 b.Skip("skipping: GOMAXPROCS=1") 661 } 662 663 wakeDelay := 5 * time.Microsecond 664 for _, delay := range []time.Duration{ 665 0, 666 1 * time.Microsecond, 667 2 * time.Microsecond, 668 5 * time.Microsecond, 669 10 * time.Microsecond, 670 20 * time.Microsecond, 671 50 * time.Microsecond, 672 100 * time.Microsecond, 673 } { 674 b.Run(delay.String(), func(b *testing.B) { 675 if b.N == 0 { 676 return 677 } 678 // Start two goroutines, which alternate between being 679 // sender and receiver in the following protocol: 680 // 681 // - The receiver spins for `delay` and then does a 682 // blocking receive on a channel. 683 // 684 // - The sender spins for `delay+wakeDelay` and then 685 // sends to the same channel. (The addition of 686 // `wakeDelay` improves the probability that the 687 // receiver will be blocking when the send occurs when 688 // the goroutines execute in parallel.) 689 // 690 // In each iteration of the benchmark, each goroutine 691 // acts once as sender and once as receiver, so each 692 // goroutine spins for delay twice. 693 // 694 // BenchmarkWakeupParallel is used to estimate how 695 // efficiently the scheduler parallelizes goroutines in 696 // the presence of blocking: 697 // 698 // - If both goroutines are executed on the same core, 699 // an increase in delay by N will increase the time per 700 // iteration by 4*N, because all 4 delays are 701 // serialized. 702 // 703 // - Otherwise, an increase in delay by N will increase 704 // the time per iteration by 2*N, and the time per 705 // iteration is 2 * (runtime overhead + chan 706 // send/receive pair + delay + wakeDelay). This allows 707 // the runtime overhead, including the time it takes 708 // for the unblocked goroutine to be scheduled, to be 709 // estimated. 710 ping, pong := make(chan struct{}), make(chan struct{}) 711 start := make(chan struct{}) 712 done := make(chan struct{}) 713 go func() { 714 <-start 715 for i := 0; i < b.N; i++ { 716 // sender 717 spin(delay + wakeDelay) 718 ping <- struct{}{} 719 // receiver 720 spin(delay) 721 <-pong 722 } 723 done <- struct{}{} 724 }() 725 go func() { 726 for i := 0; i < b.N; i++ { 727 // receiver 728 spin(delay) 729 <-ping 730 // sender 731 spin(delay + wakeDelay) 732 pong <- struct{}{} 733 } 734 done <- struct{}{} 735 }() 736 b.ResetTimer() 737 start <- struct{}{} 738 <-done 739 <-done 740 }) 741 } 742 } 743 744 func BenchmarkWakeupParallelSpinning(b *testing.B) { 745 benchmarkWakeupParallel(b, func(d time.Duration) { 746 end := time.Now().Add(d) 747 for time.Now().Before(end) { 748 // do nothing 749 } 750 }) 751 } 752 753 // sysNanosleep is defined by OS-specific files (such as runtime_linux_test.go) 754 // to sleep for the given duration. If nil, dependent tests are skipped. 755 // The implementation should invoke a blocking system call and not 756 // call time.Sleep, which would deschedule the goroutine. 757 var sysNanosleep func(d time.Duration) 758 759 func BenchmarkWakeupParallelSyscall(b *testing.B) { 760 if sysNanosleep == nil { 761 b.Skipf("skipping on %v; sysNanosleep not defined", runtime.GOOS) 762 } 763 benchmarkWakeupParallel(b, func(d time.Duration) { 764 sysNanosleep(d) 765 }) 766 } 767 768 type Matrix [][]float64 769 770 func BenchmarkMatmult(b *testing.B) { 771 b.StopTimer() 772 // matmult is O(N**3) but testing expects O(b.N), 773 // so we need to take cube root of b.N 774 n := int(math.Cbrt(float64(b.N))) + 1 775 A := makeMatrix(n) 776 B := makeMatrix(n) 777 C := makeMatrix(n) 778 b.StartTimer() 779 matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8) 780 } 781 782 func makeMatrix(n int) Matrix { 783 m := make(Matrix, n) 784 for i := 0; i < n; i++ { 785 m[i] = make([]float64, n) 786 for j := 0; j < n; j++ { 787 m[i][j] = float64(i*n + j) 788 } 789 } 790 return m 791 } 792 793 func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) { 794 di := i1 - i0 795 dj := j1 - j0 796 dk := k1 - k0 797 if di >= dj && di >= dk && di >= threshold { 798 // divide in two by y axis 799 mi := i0 + di/2 800 done1 := make(chan struct{}, 1) 801 go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold) 802 matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold) 803 <-done1 804 } else if dj >= dk && dj >= threshold { 805 // divide in two by x axis 806 mj := j0 + dj/2 807 done1 := make(chan struct{}, 1) 808 go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold) 809 matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold) 810 <-done1 811 } else if dk >= threshold { 812 // divide in two by "k" axis 813 // deliberately not parallel because of data races 814 mk := k0 + dk/2 815 matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold) 816 matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold) 817 } else { 818 // the matrices are small enough, compute directly 819 for i := i0; i < i1; i++ { 820 for j := j0; j < j1; j++ { 821 for k := k0; k < k1; k++ { 822 C[i][j] += A[i][k] * B[k][j] 823 } 824 } 825 } 826 } 827 if done != nil { 828 done <- struct{}{} 829 } 830 } 831 832 func TestStealOrder(t *testing.T) { 833 runtime.RunStealOrderTest() 834 } 835 836 func TestLockOSThreadNesting(t *testing.T) { 837 go func() { 838 e, i := runtime.LockOSCounts() 839 if e != 0 || i != 0 { 840 t.Errorf("want locked counts 0, 0; got %d, %d", e, i) 841 return 842 } 843 runtime.LockOSThread() 844 runtime.LockOSThread() 845 runtime.UnlockOSThread() 846 e, i = runtime.LockOSCounts() 847 if e != 1 || i != 0 { 848 t.Errorf("want locked counts 1, 0; got %d, %d", e, i) 849 return 850 } 851 runtime.UnlockOSThread() 852 e, i = runtime.LockOSCounts() 853 if e != 0 || i != 0 { 854 t.Errorf("want locked counts 0, 0; got %d, %d", e, i) 855 return 856 } 857 }() 858 } 859 860 func TestLockOSThreadExit(t *testing.T) { 861 testLockOSThreadExit(t, "testprog") 862 } 863 864 func testLockOSThreadExit(t *testing.T, prog string) { 865 output := runTestProg(t, prog, "LockOSThreadMain", "GOMAXPROCS=1") 866 want := "OK\n" 867 if output != want { 868 t.Errorf("want %s, got %s\n", want, output) 869 } 870 871 output = runTestProg(t, prog, "LockOSThreadAlt") 872 if output != want { 873 t.Errorf("want %s, got %s\n", want, output) 874 } 875 }