blob: 2be103e3a6b680d3984c7a18746d4031bfa0190d [file] [log] [blame]
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -04001// 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 (
Dmitriy Vyukov72b09bd2013-03-01 00:41:45 +02008 "math"
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -04009 "runtime"
Austin Clementsf2c39572015-05-26 14:32:24 -040010 "runtime/debug"
Dmitry Vyukov0e80b2e2015-01-19 22:59:58 +030011 "sync"
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -070012 "sync/atomic"
Dmitriy Vyukov4bb491b2013-06-15 16:06:28 +040013 "syscall"
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040014 "testing"
Dmitriy Vyukov6a828482013-02-15 00:02:12 +040015 "time"
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040016)
17
Russ Cox781df132011-04-22 15:22:11 -040018var stop = make(chan bool, 1)
19
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040020func perpetuumMobile() {
Russ Cox781df132011-04-22 15:22:11 -040021 select {
22 case <-stop:
23 default:
24 go perpetuumMobile()
25 }
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040026}
27
28func TestStopTheWorldDeadlock(t *testing.T) {
Russ Cox4f7fd3c2011-04-23 10:03:51 -040029 if testing.Short() {
Dave Cheney6a9e9562013-01-24 17:32:10 +110030 t.Skip("skipping during short test")
Russ Cox4f7fd3c2011-04-23 10:03:51 -040031 }
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040032 maxprocs := runtime.GOMAXPROCS(3)
33 compl := make(chan bool, 2)
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040034 go func() {
35 for i := 0; i != 1000; i += 1 {
36 runtime.GC()
37 }
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040038 compl <- true
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040039 }()
40 go func() {
41 for i := 0; i != 1000; i += 1 {
42 runtime.GOMAXPROCS(3)
43 }
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040044 compl <- true
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040045 }()
46 go perpetuumMobile()
47 <-compl
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040048 <-compl
Russ Cox781df132011-04-22 15:22:11 -040049 stop <- true
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040050 runtime.GOMAXPROCS(maxprocs)
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040051}
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -070052
Dmitriy Vyukova92e11a2013-02-20 12:13:04 +040053func TestYieldProgress(t *testing.T) {
54 testYieldProgress(t, false)
55}
56
57func TestYieldLockedProgress(t *testing.T) {
58 testYieldProgress(t, true)
59}
60
61func testYieldProgress(t *testing.T, locked bool) {
62 c := make(chan bool)
63 cack := make(chan bool)
64 go func() {
65 if locked {
66 runtime.LockOSThread()
67 }
68 for {
69 select {
70 case <-c:
71 cack <- true
72 return
73 default:
74 runtime.Gosched()
75 }
76 }
77 }()
78 time.Sleep(10 * time.Millisecond)
79 c <- true
80 <-cack
81}
82
Dmitriy Vyukov6a828482013-02-15 00:02:12 +040083func TestYieldLocked(t *testing.T) {
84 const N = 10
85 c := make(chan bool)
86 go func() {
87 runtime.LockOSThread()
88 for i := 0; i < N; i++ {
89 runtime.Gosched()
90 time.Sleep(time.Millisecond)
91 }
92 c <- true
93 // runtime.UnlockOSThread() is deliberately omitted
94 }()
95 <-c
96}
97
Dmitriy Vyukov32fef992013-07-11 15:57:36 -040098func TestGoroutineParallelism(t *testing.T) {
Russ Cox4a4eba92015-07-20 20:30:41 -040099 if runtime.NumCPU() == 1 {
100 // Takes too long, too easy to deadlock, etc.
101 t.Skip("skipping on uniprocessor")
102 }
Dmitriy Vyukovd8bbbd22013-08-01 18:25:36 +0400103 P := 4
104 N := 10
105 if testing.Short() {
106 P = 3
107 N = 3
108 }
Dmitriy Vyukov32fef992013-07-11 15:57:36 -0400109 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
Dmitriy Vyukov387c1c62014-07-15 10:30:12 +0400110 // If runtime triggers a forced GC during this test then it will deadlock,
111 // since the goroutines can't be stopped/preempted.
Austin Clementsf2c39572015-05-26 14:32:24 -0400112 // Disable GC for this test (see issue #10958).
113 defer debug.SetGCPercent(debug.SetGCPercent(-1))
Dmitriy Vyukovd8bbbd22013-08-01 18:25:36 +0400114 for try := 0; try < N; try++ {
Dmitriy Vyukov32fef992013-07-11 15:57:36 -0400115 done := make(chan bool)
116 x := uint32(0)
117 for p := 0; p < P; p++ {
118 // Test that all P goroutines are scheduled at the same time
119 go func(p int) {
120 for i := 0; i < 3; i++ {
121 expected := uint32(P*i + p)
122 for atomic.LoadUint32(&x) != expected {
123 }
124 atomic.StoreUint32(&x, expected+1)
125 }
126 done <- true
127 }(p)
128 }
129 for p := 0; p < P; p++ {
130 <-done
131 }
132 }
133}
134
Dmitriy Vyukov6a828482013-02-15 00:02:12 +0400135func TestBlockLocked(t *testing.T) {
136 const N = 10
137 c := make(chan bool)
138 go func() {
139 runtime.LockOSThread()
140 for i := 0; i < N; i++ {
141 c <- true
142 }
143 runtime.UnlockOSThread()
144 }()
145 for i := 0; i < N; i++ {
146 <-c
147 }
148}
149
Dmitriy Vyukov4bb491b2013-06-15 16:06:28 +0400150func TestTimerFairness(t *testing.T) {
151 done := make(chan bool)
152 c := make(chan bool)
153 for i := 0; i < 2; i++ {
154 go func() {
155 for {
156 select {
157 case c <- true:
158 case <-done:
159 return
160 }
161 }
162 }()
163 }
164
165 timer := time.After(20 * time.Millisecond)
166 for {
167 select {
168 case <-c:
169 case <-timer:
170 close(done)
171 return
172 }
173 }
174}
175
176func TestTimerFairness2(t *testing.T) {
177 done := make(chan bool)
178 c := make(chan bool)
179 for i := 0; i < 2; i++ {
180 go func() {
181 timer := time.After(20 * time.Millisecond)
182 var buf [1]byte
183 for {
184 syscall.Read(0, buf[0:0])
185 select {
186 case c <- true:
187 case <-c:
188 case <-timer:
189 done <- true
190 return
191 }
192 }
193 }()
194 }
195 <-done
196 <-done
197}
198
Dmitriy Vyukov1e112cd2013-06-28 17:52:17 +0400199// The function is used to test preemption at split stack checks.
200// Declaring a var avoids inlining at the call site.
201var preempt = func() int {
202 var a [128]int
203 sum := 0
204 for _, v := range a {
205 sum += v
206 }
207 return sum
208}
209
Dmitriy Vyukovbc31bcc2013-07-19 01:22:26 +0400210func TestPreemption(t *testing.T) {
Dmitriy Vyukovbc31bcc2013-07-19 01:22:26 +0400211 // Test that goroutines are preempted at function calls.
Dmitriy Vyukovd8bbbd22013-08-01 18:25:36 +0400212 N := 5
213 if testing.Short() {
214 N = 2
215 }
Dmitriy Vyukovbc31bcc2013-07-19 01:22:26 +0400216 c := make(chan bool)
217 var x uint32
218 for g := 0; g < 2; g++ {
219 go func(g int) {
220 for i := 0; i < N; i++ {
221 for atomic.LoadUint32(&x) != uint32(g) {
222 preempt()
223 }
224 atomic.StoreUint32(&x, uint32(1-g))
225 }
226 c <- true
227 }(g)
228 }
229 <-c
230 <-c
231}
232
Dmitriy Vyukov1e112cd2013-06-28 17:52:17 +0400233func TestPreemptionGC(t *testing.T) {
234 // Test that pending GC preempts running goroutines.
Dmitriy Vyukovd8bbbd22013-08-01 18:25:36 +0400235 P := 5
236 N := 10
237 if testing.Short() {
238 P = 3
239 N = 2
240 }
Dmitriy Vyukov1e112cd2013-06-28 17:52:17 +0400241 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))
242 var stop uint32
243 for i := 0; i < P; i++ {
244 go func() {
245 for atomic.LoadUint32(&stop) == 0 {
246 preempt()
247 }
248 }()
249 }
Dmitriy Vyukovd8bbbd22013-08-01 18:25:36 +0400250 for i := 0; i < N; i++ {
Dmitriy Vyukov1e112cd2013-06-28 17:52:17 +0400251 runtime.Gosched()
252 runtime.GC()
253 }
254 atomic.StoreUint32(&stop, 1)
255}
256
Dmitriy Vyukov90eca362014-01-21 10:24:42 +0400257func TestGCFairness(t *testing.T) {
258 output := executeTest(t, testGCFairnessSource, nil)
259 want := "OK\n"
260 if output != want {
261 t.Fatalf("want %s, got %s\n", want, output)
262 }
263}
264
265const testGCFairnessSource = `
266package main
267
268import (
269 "fmt"
270 "os"
271 "runtime"
272 "time"
273)
274
275func main() {
276 runtime.GOMAXPROCS(1)
277 f, err := os.Open("/dev/null")
278 if os.IsNotExist(err) {
279 // This test tests what it is intended to test only if writes are fast.
280 // If there is no /dev/null, we just don't execute the test.
Dmitriy Vyukovd76a1e52014-01-21 10:44:08 +0400281 fmt.Println("OK")
Dmitriy Vyukov90eca362014-01-21 10:24:42 +0400282 return
283 }
284 if err != nil {
285 fmt.Println(err)
286 os.Exit(1)
287 }
288 for i := 0; i < 2; i++ {
289 go func() {
290 for {
291 f.Write([]byte("."))
292 }
293 }()
294 }
295 time.Sleep(10 * time.Millisecond)
296 fmt.Println("OK")
297}
298`
299
Austin Clementse870f062015-04-22 14:42:26 -0400300func TestPingPongHog(t *testing.T) {
301 if testing.Short() {
302 t.Skip("skipping in -short mode")
303 }
304
305 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
306 done := make(chan bool)
307 hogChan, lightChan := make(chan bool), make(chan bool)
308 hogCount, lightCount := 0, 0
309
310 run := func(limit int, counter *int, wake chan bool) {
311 for {
312 select {
313 case <-done:
314 return
315
316 case <-wake:
317 for i := 0; i < limit; i++ {
318 *counter++
319 }
320 wake <- true
321 }
322 }
323 }
324
325 // Start two co-scheduled hog goroutines.
326 for i := 0; i < 2; i++ {
327 go run(1e6, &hogCount, hogChan)
328 }
329
330 // Start two co-scheduled light goroutines.
331 for i := 0; i < 2; i++ {
332 go run(1e3, &lightCount, lightChan)
333 }
334
335 // Start goroutine pairs and wait for a few preemption rounds.
336 hogChan <- true
337 lightChan <- true
338 time.Sleep(100 * time.Millisecond)
339 close(done)
340 <-hogChan
341 <-lightChan
342
343 // Check that hogCount and lightCount are within a factor of
344 // 2, which indicates that both pairs of goroutines handed off
345 // the P within a time-slice to their buddy.
346 if hogCount > lightCount*2 || lightCount > hogCount*2 {
347 t.Fatalf("want hogCount/lightCount in [0.5, 2]; got %d/%d = %g", hogCount, lightCount, float64(hogCount)/float64(lightCount))
348 }
349}
350
Austin Clementsda0e37f2015-04-22 16:38:04 -0400351func BenchmarkPingPongHog(b *testing.B) {
352 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
353
354 // Create a CPU hog
355 stop, done := make(chan bool), make(chan bool)
356 go func() {
357 for {
358 select {
359 case <-stop:
360 done <- true
361 return
362 default:
363 }
364 }
365 }()
366
367 // Ping-pong b.N times
368 ping, pong := make(chan bool), make(chan bool)
369 go func() {
370 for j := 0; j < b.N; j++ {
371 pong <- <-ping
372 }
373 close(stop)
Russ Cox42da2702015-04-27 16:08:11 -0400374 done <- true
Austin Clementsda0e37f2015-04-22 16:38:04 -0400375 }()
376 go func() {
377 for i := 0; i < b.N; i++ {
378 ping <- <-pong
379 }
Russ Cox42da2702015-04-27 16:08:11 -0400380 done <- true
Austin Clementsda0e37f2015-04-22 16:38:04 -0400381 }()
382 b.ResetTimer()
383 ping <- true // Start ping-pong
384 <-stop
385 b.StopTimer()
386 <-ping // Let last ponger exit
Russ Cox42da2702015-04-27 16:08:11 -0400387 <-done // Make sure goroutines exit
388 <-done
389 <-done
Austin Clementsda0e37f2015-04-22 16:38:04 -0400390}
391
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -0700392func stackGrowthRecursive(i int) {
393 var pad [128]uint64
394 if i != 0 && pad[0] == 0 {
395 stackGrowthRecursive(i - 1)
396 }
397}
398
Russ Cox031c1072013-07-12 12:12:56 -0400399func TestPreemptSplitBig(t *testing.T) {
400 if testing.Short() {
401 t.Skip("skipping in -short mode")
402 }
403 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(2))
404 stop := make(chan int)
405 go big(stop)
406 for i := 0; i < 3; i++ {
Dmitriy Vyukov0a86b4d2013-07-16 01:02:42 +0400407 time.Sleep(10 * time.Microsecond) // let big start running
Russ Cox031c1072013-07-12 12:12:56 -0400408 runtime.GC()
409 }
410 close(stop)
411}
412
413func big(stop chan int) int {
414 n := 0
415 for {
416 // delay so that gc is sure to have asked for a preemption
Dmitriy Vyukov0a86b4d2013-07-16 01:02:42 +0400417 for i := 0; i < 1e9; i++ {
Russ Cox031c1072013-07-12 12:12:56 -0400418 n++
419 }
420
421 // call bigframe, which used to miss the preemption in its prologue.
422 bigframe(stop)
423
424 // check if we've been asked to stop.
425 select {
426 case <-stop:
427 return n
428 }
429 }
430}
431
432func bigframe(stop chan int) int {
433 // not splitting the stack will overflow.
434 // small will notice that it needs a stack split and will
435 // catch the overflow.
436 var x [8192]byte
437 return small(stop, &x)
438}
439
440func small(stop chan int, x *[8192]byte) int {
441 for i := range x {
442 x[i] = byte(i)
443 }
444 sum := 0
445 for i := range x {
446 sum += int(x[i])
447 }
448
449 // keep small from being a leaf function, which might
450 // make it not do any stack check at all.
451 nonleaf(stop)
452
453 return sum
454}
455
456func nonleaf(stop chan int) bool {
457 // do something that won't be inlined:
458 select {
459 case <-stop:
460 return true
461 default:
462 return false
463 }
464}
465
Dmitriy Vyukov353ce602013-02-23 08:48:02 +0400466func TestSchedLocalQueue(t *testing.T) {
Keith Randalldbed4e92014-09-06 10:07:23 -0700467 runtime.RunSchedLocalQueueTest()
Dmitriy Vyukov353ce602013-02-23 08:48:02 +0400468}
469
470func TestSchedLocalQueueSteal(t *testing.T) {
Keith Randalldbed4e92014-09-06 10:07:23 -0700471 runtime.RunSchedLocalQueueStealTest()
Dmitriy Vyukov353ce602013-02-23 08:48:02 +0400472}
473
Dmitriy Vyukovf82db7d2013-01-10 09:57:06 +0400474func benchmarkStackGrowth(b *testing.B, rec int) {
Dmitriy Vyukov69257d12014-02-24 20:50:12 +0400475 b.RunParallel(func(pb *testing.PB) {
476 for pb.Next() {
477 stackGrowthRecursive(rec)
478 }
479 })
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -0700480}
Russ Cox025abd52011-07-19 11:01:17 -0400481
Dmitriy Vyukovf82db7d2013-01-10 09:57:06 +0400482func BenchmarkStackGrowth(b *testing.B) {
483 benchmarkStackGrowth(b, 10)
484}
485
486func BenchmarkStackGrowthDeep(b *testing.B) {
487 benchmarkStackGrowth(b, 1024)
488}
489
Dmitriy Vyukov5a5e6982012-06-27 21:57:49 +0400490func BenchmarkCreateGoroutines(b *testing.B) {
491 benchmarkCreateGoroutines(b, 1)
492}
493
494func BenchmarkCreateGoroutinesParallel(b *testing.B) {
495 benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1))
496}
497
498func benchmarkCreateGoroutines(b *testing.B, procs int) {
499 c := make(chan bool)
500 var f func(n int)
501 f = func(n int) {
502 if n == 0 {
503 c <- true
504 return
505 }
506 go f(n - 1)
507 }
508 for i := 0; i < procs; i++ {
509 go f(b.N / procs)
510 }
511 for i := 0; i < procs; i++ {
512 <-c
513 }
514}
Dmitriy Vyukov72b09bd2013-03-01 00:41:45 +0200515
Dmitry Vyukov0e80b2e2015-01-19 22:59:58 +0300516func BenchmarkCreateGoroutinesCapture(b *testing.B) {
517 b.ReportAllocs()
518 for i := 0; i < b.N; i++ {
519 const N = 4
520 var wg sync.WaitGroup
521 wg.Add(N)
522 for i := 0; i < N; i++ {
523 i := i
524 go func() {
525 if i >= N {
526 b.Logf("bad") // just to capture b
527 }
528 wg.Done()
529 }()
530 }
531 wg.Wait()
532 }
533}
534
Dmitry Vyukovc4ee44b2015-02-06 15:09:46 +0300535func BenchmarkClosureCall(b *testing.B) {
536 sum := 0
537 off1 := 1
538 for i := 0; i < b.N; i++ {
539 off2 := 2
540 func() {
541 sum += i + off1 + off2
542 }()
543 }
544 _ = sum
545}
546
Dmitriy Vyukov72b09bd2013-03-01 00:41:45 +0200547type Matrix [][]float64
548
549func BenchmarkMatmult(b *testing.B) {
550 b.StopTimer()
551 // matmult is O(N**3) but testing expects O(b.N),
552 // so we need to take cube root of b.N
553 n := int(math.Cbrt(float64(b.N))) + 1
554 A := makeMatrix(n)
555 B := makeMatrix(n)
556 C := makeMatrix(n)
557 b.StartTimer()
558 matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8)
559}
560
561func makeMatrix(n int) Matrix {
562 m := make(Matrix, n)
563 for i := 0; i < n; i++ {
564 m[i] = make([]float64, n)
565 for j := 0; j < n; j++ {
566 m[i][j] = float64(i*n + j)
567 }
568 }
569 return m
570}
571
572func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) {
573 di := i1 - i0
574 dj := j1 - j0
575 dk := k1 - k0
576 if di >= dj && di >= dk && di >= threshold {
577 // divide in two by y axis
578 mi := i0 + di/2
579 done1 := make(chan struct{}, 1)
580 go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold)
581 matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold)
582 <-done1
583 } else if dj >= dk && dj >= threshold {
584 // divide in two by x axis
585 mj := j0 + dj/2
586 done1 := make(chan struct{}, 1)
587 go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold)
588 matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold)
589 <-done1
590 } else if dk >= threshold {
591 // divide in two by "k" axis
592 // deliberately not parallel because of data races
593 mk := k0 + dk/2
594 matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold)
595 matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold)
596 } else {
597 // the matrices are small enough, compute directly
598 for i := i0; i < i1; i++ {
599 for j := j0; j < j1; j++ {
600 for k := k0; k < k1; k++ {
601 C[i][j] += A[i][k] * B[k][j]
602 }
603 }
604 }
605 }
606 if done != nil {
607 done <- struct{}{}
608 }
609}