Source file
src/runtime/proc_test.go
Documentation: runtime
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 for try := 0; try < N; try++ {
123 done := make(chan bool)
124 x := uint32(0)
125 for p := 0; p < P; p++ {
126
127 go func(p int) {
128 for i := 0; i < 3; i++ {
129 expected := uint32(P*i + p)
130 for atomic.LoadUint32(&x) != expected {
131 }
132 atomic.StoreUint32(&x, expected+1)
133 }
134 done <- true
135 }(p)
136 }
137 for p := 0; p < P; p++ {
138 <-done
139 }
140 }
141 }
142
143
144 func TestGoroutineParallelism2(t *testing.T) {
145
146 testGoroutineParallelism2(t, true, false)
147 testGoroutineParallelism2(t, false, true)
148 testGoroutineParallelism2(t, true, true)
149 }
150
151 func testGoroutineParallelism2(t *testing.T, load, netpoll bool) {
152 if runtime.NumCPU() == 1 {
153
154 t.Skip("skipping on uniprocessor")
155 }
156 P := 4
157 N := 10
158 if testing.Short() {
159 N = 3
160 }
161 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
162
163
164
165 defer debug.SetGCPercent(debug.SetGCPercent(-1))
166 for try := 0; try < N; try++ {
167 if load {
168
169
170
171 done := make(chan bool)
172 x := uint32(0)
173 for p := 0; p < P; p++ {
174 go func() {
175 if atomic.AddUint32(&x, 1) == uint32(P) {
176 done <- true
177 return
178 }
179 for atomic.LoadUint32(&x) != uint32(P) {
180 }
181 }()
182 }
183 <-done
184 }
185 if netpoll {
186
187 laddr := "localhost:0"
188 if runtime.GOOS == "android" {
189
190
191
192 laddr = "127.0.0.1:0"
193 }
194 ln, err := net.Listen("tcp", laddr)
195 if err != nil {
196 defer ln.Close()
197 }
198 }
199 done := make(chan bool)
200 x := uint32(0)
201
202 for p := 0; p < P/2; p++ {
203 go func(p int) {
204 for p2 := 0; p2 < 2; p2++ {
205 go func(p2 int) {
206 for i := 0; i < 3; i++ {
207 expected := uint32(P*i + p*2 + p2)
208 for atomic.LoadUint32(&x) != expected {
209 }
210 atomic.StoreUint32(&x, expected+1)
211 }
212 done <- true
213 }(p2)
214 }
215 }(p)
216 }
217 for p := 0; p < P; p++ {
218 <-done
219 }
220 }
221 }
222
223 func TestBlockLocked(t *testing.T) {
224 const N = 10
225 c := make(chan bool)
226 go func() {
227 runtime.LockOSThread()
228 for i := 0; i < N; i++ {
229 c <- true
230 }
231 runtime.UnlockOSThread()
232 }()
233 for i := 0; i < N; i++ {
234 <-c
235 }
236 }
237
238 func TestTimerFairness(t *testing.T) {
239 if runtime.GOARCH == "wasm" {
240 t.Skip("no preemption on wasm yet")
241 }
242
243 done := make(chan bool)
244 c := make(chan bool)
245 for i := 0; i < 2; i++ {
246 go func() {
247 for {
248 select {
249 case c <- true:
250 case <-done:
251 return
252 }
253 }
254 }()
255 }
256
257 timer := time.After(20 * time.Millisecond)
258 for {
259 select {
260 case <-c:
261 case <-timer:
262 close(done)
263 return
264 }
265 }
266 }
267
268 func TestTimerFairness2(t *testing.T) {
269 if runtime.GOARCH == "wasm" {
270 t.Skip("no preemption on wasm yet")
271 }
272
273 done := make(chan bool)
274 c := make(chan bool)
275 for i := 0; i < 2; i++ {
276 go func() {
277 timer := time.After(20 * time.Millisecond)
278 var buf [1]byte
279 for {
280 syscall.Read(0, buf[0:0])
281 select {
282 case c <- true:
283 case <-c:
284 case <-timer:
285 done <- true
286 return
287 }
288 }
289 }()
290 }
291 <-done
292 <-done
293 }
294
295
296
297 var preempt = func() int {
298 var a [128]int
299 sum := 0
300 for _, v := range a {
301 sum += v
302 }
303 return sum
304 }
305
306 func TestPreemption(t *testing.T) {
307 if runtime.GOARCH == "wasm" {
308 t.Skip("no preemption on wasm yet")
309 }
310
311
312 N := 5
313 if testing.Short() {
314 N = 2
315 }
316 c := make(chan bool)
317 var x uint32
318 for g := 0; g < 2; g++ {
319 go func(g int) {
320 for i := 0; i < N; i++ {
321 for atomic.LoadUint32(&x) != uint32(g) {
322 preempt()
323 }
324 atomic.StoreUint32(&x, uint32(1-g))
325 }
326 c <- true
327 }(g)
328 }
329 <-c
330 <-c
331 }
332
333 func TestPreemptionGC(t *testing.T) {
334 if runtime.GOARCH == "wasm" {
335 t.Skip("no preemption on wasm yet")
336 }
337
338
339 P := 5
340 N := 10
341 if testing.Short() {
342 P = 3
343 N = 2
344 }
345 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))
346 var stop uint32
347 for i := 0; i < P; i++ {
348 go func() {
349 for atomic.LoadUint32(&stop) == 0 {
350 preempt()
351 }
352 }()
353 }
354 for i := 0; i < N; i++ {
355 runtime.Gosched()
356 runtime.GC()
357 }
358 atomic.StoreUint32(&stop, 1)
359 }
360
361 func TestAsyncPreempt(t *testing.T) {
362 if !runtime.PreemptMSupported {
363 t.Skip("asynchronous preemption not supported on this platform")
364 }
365 output := runTestProg(t, "testprog", "AsyncPreempt")
366 want := "OK\n"
367 if output != want {
368 t.Fatalf("want %s, got %s\n", want, output)
369 }
370 }
371
372 func TestGCFairness(t *testing.T) {
373 output := runTestProg(t, "testprog", "GCFairness")
374 want := "OK\n"
375 if output != want {
376 t.Fatalf("want %s, got %s\n", want, output)
377 }
378 }
379
380 func TestGCFairness2(t *testing.T) {
381 output := runTestProg(t, "testprog", "GCFairness2")
382 want := "OK\n"
383 if output != want {
384 t.Fatalf("want %s, got %s\n", want, output)
385 }
386 }
387
388 func TestNumGoroutine(t *testing.T) {
389 output := runTestProg(t, "testprog", "NumGoroutine")
390 want := "1\n"
391 if output != want {
392 t.Fatalf("want %q, got %q", want, output)
393 }
394
395 buf := make([]byte, 1<<20)
396
397
398
399
400 for i := 0; ; i++ {
401
402
403
404
405 runtime.Gosched()
406
407 n := runtime.NumGoroutine()
408 buf = buf[:runtime.Stack(buf, true)]
409
410 nstk := strings.Count(string(buf), "goroutine ")
411 if n == nstk {
412 break
413 }
414 if i >= 10 {
415 t.Fatalf("NumGoroutine=%d, but found %d goroutines in stack dump: %s", n, nstk, buf)
416 }
417 }
418 }
419
420 func TestPingPongHog(t *testing.T) {
421 if runtime.GOARCH == "wasm" {
422 t.Skip("no preemption on wasm yet")
423 }
424 if testing.Short() {
425 t.Skip("skipping in -short mode")
426 }
427 if race.Enabled {
428
429
430 t.Skip("skipping in -race mode")
431 }
432
433 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
434 done := make(chan bool)
435 hogChan, lightChan := make(chan bool), make(chan bool)
436 hogCount, lightCount := 0, 0
437
438 run := func(limit int, counter *int, wake chan bool) {
439 for {
440 select {
441 case <-done:
442 return
443
444 case <-wake:
445 for i := 0; i < limit; i++ {
446 *counter++
447 }
448 wake <- true
449 }
450 }
451 }
452
453
454 for i := 0; i < 2; i++ {
455 go run(1e6, &hogCount, hogChan)
456 }
457
458
459 for i := 0; i < 2; i++ {
460 go run(1e3, &lightCount, lightChan)
461 }
462
463
464 hogChan <- true
465 lightChan <- true
466 time.Sleep(100 * time.Millisecond)
467 close(done)
468 <-hogChan
469 <-lightChan
470
471
472
473
474
475
476 const factor = 5
477 if hogCount > lightCount*factor || lightCount > hogCount*factor {
478 t.Fatalf("want hogCount/lightCount in [%v, %v]; got %d/%d = %g", 1.0/factor, factor, hogCount, lightCount, float64(hogCount)/float64(lightCount))
479 }
480 }
481
482 func BenchmarkPingPongHog(b *testing.B) {
483 if b.N == 0 {
484 return
485 }
486 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
487
488
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
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
518 <-stop
519 b.StopTimer()
520 <-ping
521 <-done
522 <-done
523 <-done
524 }
525
526 var padData [128]uint64
527
528 func stackGrowthRecursive(i int) {
529 var pad [128]uint64
530 pad = padData
531 for j := range pad {
532 if pad[j] != 0 {
533 return
534 }
535 }
536 if i != 0 {
537 stackGrowthRecursive(i - 1)
538 }
539 }
540
541 func TestPreemptSplitBig(t *testing.T) {
542 if testing.Short() {
543 t.Skip("skipping in -short mode")
544 }
545 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
546 stop := make(chan int)
547 go big(stop)
548 for i := 0; i < 3; i++ {
549 time.Sleep(10 * time.Microsecond)
550 runtime.GC()
551 }
552 close(stop)
553 }
554
555 func big(stop chan int) int {
556 n := 0
557 for {
558
559 for i := 0; i < 1e9; i++ {
560 n++
561 }
562
563
564 bigframe(stop)
565
566
567 select {
568 case <-stop:
569 return n
570 }
571 }
572 }
573
574 func bigframe(stop chan int) int {
575
576
577
578 var x [8192]byte
579 return small(stop, &x)
580 }
581
582 func small(stop chan int, x *[8192]byte) int {
583 for i := range x {
584 x[i] = byte(i)
585 }
586 sum := 0
587 for i := range x {
588 sum += int(x[i])
589 }
590
591
592
593 nonleaf(stop)
594
595 return sum
596 }
597
598 func nonleaf(stop chan int) bool {
599
600 select {
601 case <-stop:
602 return true
603 default:
604 return false
605 }
606 }
607
608 func TestSchedLocalQueue(t *testing.T) {
609 runtime.RunSchedLocalQueueTest()
610 }
611
612 func TestSchedLocalQueueSteal(t *testing.T) {
613 runtime.RunSchedLocalQueueStealTest()
614 }
615
616 func TestSchedLocalQueueEmpty(t *testing.T) {
617 if runtime.NumCPU() == 1 {
618
619 t.Skip("skipping on uniprocessor")
620 }
621 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
622
623
624
625 defer debug.SetGCPercent(debug.SetGCPercent(-1))
626
627 iters := int(1e5)
628 if testing.Short() {
629 iters = 1e2
630 }
631 runtime.RunSchedLocalQueueEmptyTest(iters)
632 }
633
634 func benchmarkStackGrowth(b *testing.B, rec int) {
635 b.RunParallel(func(pb *testing.PB) {
636 for pb.Next() {
637 stackGrowthRecursive(rec)
638 }
639 })
640 }
641
642 func BenchmarkStackGrowth(b *testing.B) {
643 benchmarkStackGrowth(b, 10)
644 }
645
646 func BenchmarkStackGrowthDeep(b *testing.B) {
647 benchmarkStackGrowth(b, 1024)
648 }
649
650 func BenchmarkCreateGoroutines(b *testing.B) {
651 benchmarkCreateGoroutines(b, 1)
652 }
653
654 func BenchmarkCreateGoroutinesParallel(b *testing.B) {
655 benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1))
656 }
657
658 func benchmarkCreateGoroutines(b *testing.B, procs int) {
659 c := make(chan bool)
660 var f func(n int)
661 f = func(n int) {
662 if n == 0 {
663 c <- true
664 return
665 }
666 go f(n - 1)
667 }
668 for i := 0; i < procs; i++ {
669 go f(b.N / procs)
670 }
671 for i := 0; i < procs; i++ {
672 <-c
673 }
674 }
675
676 func BenchmarkCreateGoroutinesCapture(b *testing.B) {
677 b.ReportAllocs()
678 for i := 0; i < b.N; i++ {
679 const N = 4
680 var wg sync.WaitGroup
681 wg.Add(N)
682 for i := 0; i < N; i++ {
683 i := i
684 go func() {
685 if i >= N {
686 b.Logf("bad")
687 }
688 wg.Done()
689 }()
690 }
691 wg.Wait()
692 }
693 }
694
695
696
697 func warmupScheduler(targetThreadCount int) {
698 var wg sync.WaitGroup
699 var count int32
700 for i := 0; i < targetThreadCount; i++ {
701 wg.Add(1)
702 go func() {
703 atomic.AddInt32(&count, 1)
704 for atomic.LoadInt32(&count) < int32(targetThreadCount) {
705
706 }
707
708
709 doWork(time.Millisecond)
710 wg.Done()
711 }()
712 }
713 wg.Wait()
714 }
715
716 func doWork(dur time.Duration) {
717 start := time.Now()
718 for time.Since(start) < dur {
719 }
720 }
721
722
723
724
725
726
727
728 func BenchmarkCreateGoroutinesSingle(b *testing.B) {
729
730
731 warmupScheduler(runtime.GOMAXPROCS(0))
732 b.ResetTimer()
733
734 var wg sync.WaitGroup
735 wg.Add(b.N)
736 for i := 0; i < b.N; i++ {
737 go func() {
738 wg.Done()
739 }()
740 }
741 wg.Wait()
742 }
743
744 func BenchmarkClosureCall(b *testing.B) {
745 sum := 0
746 off1 := 1
747 for i := 0; i < b.N; i++ {
748 off2 := 2
749 func() {
750 sum += i + off1 + off2
751 }()
752 }
753 _ = sum
754 }
755
756 func benchmarkWakeupParallel(b *testing.B, spin func(time.Duration)) {
757 if runtime.GOMAXPROCS(0) == 1 {
758 b.Skip("skipping: GOMAXPROCS=1")
759 }
760
761 wakeDelay := 5 * time.Microsecond
762 for _, delay := range []time.Duration{
763 0,
764 1 * time.Microsecond,
765 2 * time.Microsecond,
766 5 * time.Microsecond,
767 10 * time.Microsecond,
768 20 * time.Microsecond,
769 50 * time.Microsecond,
770 100 * time.Microsecond,
771 } {
772 b.Run(delay.String(), func(b *testing.B) {
773 if b.N == 0 {
774 return
775 }
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808 ping, pong := make(chan struct{}), make(chan struct{})
809 start := make(chan struct{})
810 done := make(chan struct{})
811 go func() {
812 <-start
813 for i := 0; i < b.N; i++ {
814
815 spin(delay + wakeDelay)
816 ping <- struct{}{}
817
818 spin(delay)
819 <-pong
820 }
821 done <- struct{}{}
822 }()
823 go func() {
824 for i := 0; i < b.N; i++ {
825
826 spin(delay)
827 <-ping
828
829 spin(delay + wakeDelay)
830 pong <- struct{}{}
831 }
832 done <- struct{}{}
833 }()
834 b.ResetTimer()
835 start <- struct{}{}
836 <-done
837 <-done
838 })
839 }
840 }
841
842 func BenchmarkWakeupParallelSpinning(b *testing.B) {
843 benchmarkWakeupParallel(b, func(d time.Duration) {
844 end := time.Now().Add(d)
845 for time.Now().Before(end) {
846
847 }
848 })
849 }
850
851
852
853
854
855 var sysNanosleep func(d time.Duration)
856
857 func BenchmarkWakeupParallelSyscall(b *testing.B) {
858 if sysNanosleep == nil {
859 b.Skipf("skipping on %v; sysNanosleep not defined", runtime.GOOS)
860 }
861 benchmarkWakeupParallel(b, func(d time.Duration) {
862 sysNanosleep(d)
863 })
864 }
865
866 type Matrix [][]float64
867
868 func BenchmarkMatmult(b *testing.B) {
869 b.StopTimer()
870
871
872 n := int(math.Cbrt(float64(b.N))) + 1
873 A := makeMatrix(n)
874 B := makeMatrix(n)
875 C := makeMatrix(n)
876 b.StartTimer()
877 matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8)
878 }
879
880 func makeMatrix(n int) Matrix {
881 m := make(Matrix, n)
882 for i := 0; i < n; i++ {
883 m[i] = make([]float64, n)
884 for j := 0; j < n; j++ {
885 m[i][j] = float64(i*n + j)
886 }
887 }
888 return m
889 }
890
891 func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) {
892 di := i1 - i0
893 dj := j1 - j0
894 dk := k1 - k0
895 if di >= dj && di >= dk && di >= threshold {
896
897 mi := i0 + di/2
898 done1 := make(chan struct{}, 1)
899 go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold)
900 matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold)
901 <-done1
902 } else if dj >= dk && dj >= threshold {
903
904 mj := j0 + dj/2
905 done1 := make(chan struct{}, 1)
906 go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold)
907 matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold)
908 <-done1
909 } else if dk >= threshold {
910
911
912 mk := k0 + dk/2
913 matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold)
914 matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold)
915 } else {
916
917 for i := i0; i < i1; i++ {
918 for j := j0; j < j1; j++ {
919 for k := k0; k < k1; k++ {
920 C[i][j] += A[i][k] * B[k][j]
921 }
922 }
923 }
924 }
925 if done != nil {
926 done <- struct{}{}
927 }
928 }
929
930 func TestStealOrder(t *testing.T) {
931 runtime.RunStealOrderTest()
932 }
933
934 func TestLockOSThreadNesting(t *testing.T) {
935 if runtime.GOARCH == "wasm" {
936 t.Skip("no threads on wasm yet")
937 }
938
939 go func() {
940 e, i := runtime.LockOSCounts()
941 if e != 0 || i != 0 {
942 t.Errorf("want locked counts 0, 0; got %d, %d", e, i)
943 return
944 }
945 runtime.LockOSThread()
946 runtime.LockOSThread()
947 runtime.UnlockOSThread()
948 e, i = runtime.LockOSCounts()
949 if e != 1 || i != 0 {
950 t.Errorf("want locked counts 1, 0; got %d, %d", e, i)
951 return
952 }
953 runtime.UnlockOSThread()
954 e, i = runtime.LockOSCounts()
955 if e != 0 || i != 0 {
956 t.Errorf("want locked counts 0, 0; got %d, %d", e, i)
957 return
958 }
959 }()
960 }
961
962 func TestLockOSThreadExit(t *testing.T) {
963 testLockOSThreadExit(t, "testprog")
964 }
965
966 func testLockOSThreadExit(t *testing.T, prog string) {
967 output := runTestProg(t, prog, "LockOSThreadMain", "GOMAXPROCS=1")
968 want := "OK\n"
969 if output != want {
970 t.Errorf("want %q, got %q", want, output)
971 }
972
973 output = runTestProg(t, prog, "LockOSThreadAlt")
974 if output != want {
975 t.Errorf("want %q, got %q", want, output)
976 }
977 }
978
979 func TestLockOSThreadAvoidsStatePropagation(t *testing.T) {
980 want := "OK\n"
981 skip := "unshare not permitted\n"
982 output := runTestProg(t, "testprog", "LockOSThreadAvoidsStatePropagation", "GOMAXPROCS=1")
983 if output == skip {
984 t.Skip("unshare syscall not permitted on this system")
985 } else if output != want {
986 t.Errorf("want %q, got %q", want, output)
987 }
988 }
989
990 func TestLockOSThreadTemplateThreadRace(t *testing.T) {
991 testenv.MustHaveGoRun(t)
992
993 exe, err := buildTestProg(t, "testprog")
994 if err != nil {
995 t.Fatal(err)
996 }
997
998 iterations := 100
999 if testing.Short() {
1000
1001
1002 iterations = 5
1003 }
1004 for i := 0; i < iterations; i++ {
1005 want := "OK\n"
1006 output := runBuiltTestProg(t, exe, "LockOSThreadTemplateThreadRace")
1007 if output != want {
1008 t.Fatalf("run %d: want %q, got %q", i, want, output)
1009 }
1010 }
1011 }
1012
1013
1014
1015 func fakeSyscall(duration time.Duration) {
1016 runtime.Entersyscall()
1017 for start := runtime.Nanotime(); runtime.Nanotime()-start < int64(duration); {
1018 }
1019 runtime.Exitsyscall()
1020 }
1021
1022
1023 func testPreemptionAfterSyscall(t *testing.T, syscallDuration time.Duration) {
1024 if runtime.GOARCH == "wasm" {
1025 t.Skip("no preemption on wasm yet")
1026 }
1027
1028 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
1029
1030 interations := 10
1031 if testing.Short() {
1032 interations = 1
1033 }
1034 const (
1035 maxDuration = 3 * time.Second
1036 nroutines = 8
1037 )
1038
1039 for i := 0; i < interations; i++ {
1040 c := make(chan bool, nroutines)
1041 stop := uint32(0)
1042
1043 start := time.Now()
1044 for g := 0; g < nroutines; g++ {
1045 go func(stop *uint32) {
1046 c <- true
1047 for atomic.LoadUint32(stop) == 0 {
1048 fakeSyscall(syscallDuration)
1049 }
1050 c <- true
1051 }(&stop)
1052 }
1053
1054 for g := 0; g < nroutines; g++ {
1055 <-c
1056 }
1057 atomic.StoreUint32(&stop, 1)
1058
1059 for g := 0; g < nroutines; g++ {
1060 <-c
1061 }
1062 duration := time.Since(start)
1063
1064 if duration > maxDuration {
1065 t.Errorf("timeout exceeded: %v (%v)", duration, maxDuration)
1066 }
1067 }
1068 }
1069
1070 func TestPreemptionAfterSyscall(t *testing.T) {
1071 for _, i := range []time.Duration{10, 100, 1000} {
1072 d := i * time.Microsecond
1073 t.Run(fmt.Sprint(d), func(t *testing.T) {
1074 testPreemptionAfterSyscall(t, d)
1075 })
1076 }
1077 }
1078
1079 func TestGetgThreadSwitch(t *testing.T) {
1080 runtime.RunGetgThreadSwitchTest()
1081 }
1082
1083
1084
1085
1086
1087 func TestNetpollBreak(t *testing.T) {
1088 if runtime.GOMAXPROCS(0) == 1 {
1089 t.Skip("skipping: GOMAXPROCS=1")
1090 }
1091
1092
1093 runtime.NetpollGenericInit()
1094
1095 start := time.Now()
1096 c := make(chan bool, 2)
1097 go func() {
1098 c <- true
1099 runtime.Netpoll(10 * time.Second.Nanoseconds())
1100 c <- true
1101 }()
1102 <-c
1103
1104
1105
1106 loop:
1107 for {
1108 runtime.Usleep(100)
1109 runtime.NetpollBreak()
1110 runtime.NetpollBreak()
1111 select {
1112 case <-c:
1113 break loop
1114 default:
1115 }
1116 }
1117 if dur := time.Since(start); dur > 5*time.Second {
1118 t.Errorf("netpollBreak did not interrupt netpoll: slept for: %v", dur)
1119 }
1120 }
1121
1122
1123
1124 func TestBigGOMAXPROCS(t *testing.T) {
1125 t.Parallel()
1126 output := runTestProg(t, "testprog", "NonexistentTest", "GOMAXPROCS=1024")
1127
1128 for _, errstr := range []string{
1129 "failed to create new OS thread",
1130 "cannot allocate memory",
1131 } {
1132 if strings.Contains(output, errstr) {
1133 t.Skipf("failed to create 1024 threads")
1134 }
1135 }
1136 if !strings.Contains(output, "unknown function: NonexistentTest") {
1137 t.Errorf("output:\n%s\nwanted:\nunknown function: NonexistentTest", output)
1138 }
1139 }
1140
View as plain text