Source file
src/runtime/proc_test.go
1
2
3
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
102 }()
103 <-c
104 }
105
106 func TestGoroutineParallelism(t *testing.T) {
107 if runtime.NumCPU() == 1 {
108
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
119
120
121 defer debug.SetGCPercent(debug.SetGCPercent(-1))
122
123
124
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
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
148 func TestGoroutineParallelism2(t *testing.T) {
149
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
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
167
168
169 defer debug.SetGCPercent(debug.SetGCPercent(-1))
170
171
172
173 runtime.GC()
174 for try := 0; try < N; try++ {
175 if load {
176
177
178
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
195 laddr := "localhost:0"
196 if runtime.GOOS == "android" {
197
198
199
200 laddr = "127.0.0.1:0"
201 }
202 ln, err := net.Listen("tcp", laddr)
203 if err != nil {
204 defer ln.Close()
205 }
206 }
207 done := make(chan bool)
208 x := uint32(0)
209
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
304
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
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
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
406
407
408 for i := 0; ; i++ {
409
410
411
412
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
437
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
462 for i := 0; i < 2; i++ {
463 go run(1e6, &hogCount, hogChan)
464 }
465
466
467 for i := 0; i < 2; i++ {
468 go run(1e3, &lightCount, lightChan)
469 }
470
471
472 hogChan <- true
473 lightChan <- true
474 time.Sleep(100 * time.Millisecond)
475 close(done)
476 <-hogChan
477 <-lightChan
478
479
480
481
482
483
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
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
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
526 <-stop
527 b.StopTimer()
528 <-ping
529 <-done
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)
558 runtime.GC()
559 }
560 close(stop)
561 }
562
563 func big(stop chan int) int {
564 n := 0
565 for {
566
567 for i := 0; i < 1e9; i++ {
568 n++
569 }
570
571
572 bigframe(stop)
573
574
575 select {
576 case <-stop:
577 return n
578 }
579 }
580 }
581
582 func bigframe(stop chan int) int {
583
584
585
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
600
601 nonleaf(stop)
602
603 return sum
604 }
605
606 func nonleaf(stop chan int) bool {
607
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
627 t.Skip("skipping on uniprocessor")
628 }
629 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
630
631
632
633 defer debug.SetGCPercent(debug.SetGCPercent(-1))
634
635
636
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")
699 }
700 wg.Done()
701 }()
702 }
703 wg.Wait()
704 }
705 }
706
707
708
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
718 }
719
720
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
735
736
737
738
739
740 func BenchmarkCreateGoroutinesSingle(b *testing.B) {
741
742
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
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
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
827 spin(delay + wakeDelay)
828 ping <- struct{}{}
829
830 spin(delay)
831 <-pong
832 }
833 done <- struct{}{}
834 }()
835 go func() {
836 for i := 0; i < b.N; i++ {
837
838 spin(delay)
839 <-ping
840
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
859 }
860 })
861 }
862
863
864
865
866
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
883
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
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
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
923
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
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
1013
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
1026
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
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
1066 for g := 0; g < nroutines; g++ {
1067 <-c
1068 }
1069 atomic.StoreUint32(&stop, 1)
1070
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
1100
1101
1102
1103 func TestNetpollBreak(t *testing.T) {
1104 if runtime.GOMAXPROCS(0) == 1 {
1105 t.Skip("skipping: GOMAXPROCS=1")
1106 }
1107
1108
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
1120
1121
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
1139
1140 func TestBigGOMAXPROCS(t *testing.T) {
1141 t.Parallel()
1142 output := runTestProg(t, "testprog", "NonexistentTest", "GOMAXPROCS=1024")
1143
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