blob: 8f3b407375a91d659cf1ea630b95611e99a090e5 [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"
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -070010 "sync/atomic"
Dmitriy Vyukov4bb491b2013-06-15 16:06:28 +040011 "syscall"
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040012 "testing"
Dmitriy Vyukov6a828482013-02-15 00:02:12 +040013 "time"
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040014)
15
Russ Cox781df132011-04-22 15:22:11 -040016var stop = make(chan bool, 1)
17
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040018func perpetuumMobile() {
Russ Cox781df132011-04-22 15:22:11 -040019 select {
20 case <-stop:
21 default:
22 go perpetuumMobile()
23 }
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040024}
25
26func TestStopTheWorldDeadlock(t *testing.T) {
Russ Cox4f7fd3c2011-04-23 10:03:51 -040027 if testing.Short() {
Dave Cheney6a9e9562013-01-24 17:32:10 +110028 t.Skip("skipping during short test")
Russ Cox4f7fd3c2011-04-23 10:03:51 -040029 }
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040030 maxprocs := runtime.GOMAXPROCS(3)
31 compl := make(chan bool, 2)
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040032 go func() {
33 for i := 0; i != 1000; i += 1 {
34 runtime.GC()
35 }
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040036 compl <- true
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040037 }()
38 go func() {
39 for i := 0; i != 1000; i += 1 {
40 runtime.GOMAXPROCS(3)
41 }
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040042 compl <- true
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040043 }()
44 go perpetuumMobile()
45 <-compl
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040046 <-compl
Russ Cox781df132011-04-22 15:22:11 -040047 stop <- true
Dmitriy Vyukov91cc1e62011-05-31 10:38:51 -040048 runtime.GOMAXPROCS(maxprocs)
Dmitriy Vyukov29d78f12011-04-21 12:09:25 -040049}
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -070050
Dmitriy Vyukova92e11a2013-02-20 12:13:04 +040051func TestYieldProgress(t *testing.T) {
52 testYieldProgress(t, false)
53}
54
55func TestYieldLockedProgress(t *testing.T) {
56 testYieldProgress(t, true)
57}
58
59func testYieldProgress(t *testing.T, locked bool) {
60 c := make(chan bool)
61 cack := make(chan bool)
62 go func() {
63 if locked {
64 runtime.LockOSThread()
65 }
66 for {
67 select {
68 case <-c:
69 cack <- true
70 return
71 default:
72 runtime.Gosched()
73 }
74 }
75 }()
76 time.Sleep(10 * time.Millisecond)
77 c <- true
78 <-cack
79}
80
Dmitriy Vyukov6a828482013-02-15 00:02:12 +040081func TestYieldLocked(t *testing.T) {
82 const N = 10
83 c := make(chan bool)
84 go func() {
85 runtime.LockOSThread()
86 for i := 0; i < N; i++ {
87 runtime.Gosched()
88 time.Sleep(time.Millisecond)
89 }
90 c <- true
91 // runtime.UnlockOSThread() is deliberately omitted
92 }()
93 <-c
94}
95
Dmitriy Vyukov32fef992013-07-11 15:57:36 -040096func TestGoroutineParallelism(t *testing.T) {
97 const P = 4
98 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P))
99 for try := 0; try < 10; try++ {
100 done := make(chan bool)
101 x := uint32(0)
102 for p := 0; p < P; p++ {
103 // Test that all P goroutines are scheduled at the same time
104 go func(p int) {
105 for i := 0; i < 3; i++ {
106 expected := uint32(P*i + p)
107 for atomic.LoadUint32(&x) != expected {
108 }
109 atomic.StoreUint32(&x, expected+1)
110 }
111 done <- true
112 }(p)
113 }
114 for p := 0; p < P; p++ {
115 <-done
116 }
117 }
118}
119
Dmitriy Vyukov6a828482013-02-15 00:02:12 +0400120func TestBlockLocked(t *testing.T) {
121 const N = 10
122 c := make(chan bool)
123 go func() {
124 runtime.LockOSThread()
125 for i := 0; i < N; i++ {
126 c <- true
127 }
128 runtime.UnlockOSThread()
129 }()
130 for i := 0; i < N; i++ {
131 <-c
132 }
133}
134
Dmitriy Vyukov4bb491b2013-06-15 16:06:28 +0400135func TestTimerFairness(t *testing.T) {
136 done := make(chan bool)
137 c := make(chan bool)
138 for i := 0; i < 2; i++ {
139 go func() {
140 for {
141 select {
142 case c <- true:
143 case <-done:
144 return
145 }
146 }
147 }()
148 }
149
150 timer := time.After(20 * time.Millisecond)
151 for {
152 select {
153 case <-c:
154 case <-timer:
155 close(done)
156 return
157 }
158 }
159}
160
161func TestTimerFairness2(t *testing.T) {
162 done := make(chan bool)
163 c := make(chan bool)
164 for i := 0; i < 2; i++ {
165 go func() {
166 timer := time.After(20 * time.Millisecond)
167 var buf [1]byte
168 for {
169 syscall.Read(0, buf[0:0])
170 select {
171 case c <- true:
172 case <-c:
173 case <-timer:
174 done <- true
175 return
176 }
177 }
178 }()
179 }
180 <-done
181 <-done
182}
183
Dmitriy Vyukov1e112cd2013-06-28 17:52:17 +0400184// The function is used to test preemption at split stack checks.
185// Declaring a var avoids inlining at the call site.
186var preempt = func() int {
187 var a [128]int
188 sum := 0
189 for _, v := range a {
190 sum += v
191 }
192 return sum
193}
194
195func TestPreemptionGC(t *testing.T) {
Russ Cox11844072013-07-01 18:10:03 -0400196 t.Skip("preemption is disabled")
Dmitriy Vyukov1e112cd2013-06-28 17:52:17 +0400197 // Test that pending GC preempts running goroutines.
198 const P = 5
199 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))
200 var stop uint32
201 for i := 0; i < P; i++ {
202 go func() {
203 for atomic.LoadUint32(&stop) == 0 {
204 preempt()
205 }
206 }()
207 }
208 for i := 0; i < 10; i++ {
209 runtime.Gosched()
210 runtime.GC()
211 }
212 atomic.StoreUint32(&stop, 1)
213}
214
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -0700215func stackGrowthRecursive(i int) {
216 var pad [128]uint64
217 if i != 0 && pad[0] == 0 {
218 stackGrowthRecursive(i - 1)
219 }
220}
221
Dmitriy Vyukov353ce602013-02-23 08:48:02 +0400222func TestSchedLocalQueue(t *testing.T) {
223 runtime.TestSchedLocalQueue1()
224}
225
226func TestSchedLocalQueueSteal(t *testing.T) {
227 runtime.TestSchedLocalQueueSteal1()
228}
229
Dmitriy Vyukovf82db7d2013-01-10 09:57:06 +0400230func benchmarkStackGrowth(b *testing.B, rec int) {
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -0700231 const CallsPerSched = 1000
232 procs := runtime.GOMAXPROCS(-1)
233 N := int32(b.N / CallsPerSched)
234 c := make(chan bool, procs)
235 for p := 0; p < procs; p++ {
236 go func() {
237 for atomic.AddInt32(&N, -1) >= 0 {
238 runtime.Gosched()
239 for g := 0; g < CallsPerSched; g++ {
Dmitriy Vyukovf82db7d2013-01-10 09:57:06 +0400240 stackGrowthRecursive(rec)
Dmitriy Vyukovc9152a82011-07-12 09:24:32 -0700241 }
242 }
243 c <- true
244 }()
245 }
246 for p := 0; p < procs; p++ {
247 <-c
248 }
249}
Russ Cox025abd52011-07-19 11:01:17 -0400250
Dmitriy Vyukovf82db7d2013-01-10 09:57:06 +0400251func BenchmarkStackGrowth(b *testing.B) {
252 benchmarkStackGrowth(b, 10)
253}
254
255func BenchmarkStackGrowthDeep(b *testing.B) {
256 benchmarkStackGrowth(b, 1024)
257}
258
Russ Cox025abd52011-07-19 11:01:17 -0400259func BenchmarkSyscall(b *testing.B) {
Dmitriy Vyukov38d4d3c2013-03-01 01:10:34 +0200260 benchmarkSyscall(b, 0, 1)
Russ Cox025abd52011-07-19 11:01:17 -0400261}
262
263func BenchmarkSyscallWork(b *testing.B) {
Dmitriy Vyukov38d4d3c2013-03-01 01:10:34 +0200264 benchmarkSyscall(b, 100, 1)
265}
266
267func BenchmarkSyscallExcess(b *testing.B) {
268 benchmarkSyscall(b, 0, 4)
269}
270
271func BenchmarkSyscallExcessWork(b *testing.B) {
272 benchmarkSyscall(b, 100, 4)
273}
274
275func benchmarkSyscall(b *testing.B, work, excess int) {
Russ Cox025abd52011-07-19 11:01:17 -0400276 const CallsPerSched = 1000
Dmitriy Vyukov38d4d3c2013-03-01 01:10:34 +0200277 procs := runtime.GOMAXPROCS(-1) * excess
Russ Cox025abd52011-07-19 11:01:17 -0400278 N := int32(b.N / CallsPerSched)
279 c := make(chan bool, procs)
280 for p := 0; p < procs; p++ {
281 go func() {
282 foo := 42
283 for atomic.AddInt32(&N, -1) >= 0 {
284 runtime.Gosched()
285 for g := 0; g < CallsPerSched; g++ {
286 runtime.Entersyscall()
Dmitriy Vyukov38d4d3c2013-03-01 01:10:34 +0200287 for i := 0; i < work; i++ {
Russ Cox025abd52011-07-19 11:01:17 -0400288 foo *= 2
289 foo /= 2
290 }
291 runtime.Exitsyscall()
292 }
293 }
294 c <- foo == 42
295 }()
296 }
297 for p := 0; p < procs; p++ {
298 <-c
299 }
300}
Dmitriy Vyukov5a5e6982012-06-27 21:57:49 +0400301
302func BenchmarkCreateGoroutines(b *testing.B) {
303 benchmarkCreateGoroutines(b, 1)
304}
305
306func BenchmarkCreateGoroutinesParallel(b *testing.B) {
307 benchmarkCreateGoroutines(b, runtime.GOMAXPROCS(-1))
308}
309
310func benchmarkCreateGoroutines(b *testing.B, procs int) {
311 c := make(chan bool)
312 var f func(n int)
313 f = func(n int) {
314 if n == 0 {
315 c <- true
316 return
317 }
318 go f(n - 1)
319 }
320 for i := 0; i < procs; i++ {
321 go f(b.N / procs)
322 }
323 for i := 0; i < procs; i++ {
324 <-c
325 }
326}
Dmitriy Vyukov72b09bd2013-03-01 00:41:45 +0200327
328type Matrix [][]float64
329
330func BenchmarkMatmult(b *testing.B) {
331 b.StopTimer()
332 // matmult is O(N**3) but testing expects O(b.N),
333 // so we need to take cube root of b.N
334 n := int(math.Cbrt(float64(b.N))) + 1
335 A := makeMatrix(n)
336 B := makeMatrix(n)
337 C := makeMatrix(n)
338 b.StartTimer()
339 matmult(nil, A, B, C, 0, n, 0, n, 0, n, 8)
340}
341
342func makeMatrix(n int) Matrix {
343 m := make(Matrix, n)
344 for i := 0; i < n; i++ {
345 m[i] = make([]float64, n)
346 for j := 0; j < n; j++ {
347 m[i][j] = float64(i*n + j)
348 }
349 }
350 return m
351}
352
353func matmult(done chan<- struct{}, A, B, C Matrix, i0, i1, j0, j1, k0, k1, threshold int) {
354 di := i1 - i0
355 dj := j1 - j0
356 dk := k1 - k0
357 if di >= dj && di >= dk && di >= threshold {
358 // divide in two by y axis
359 mi := i0 + di/2
360 done1 := make(chan struct{}, 1)
361 go matmult(done1, A, B, C, i0, mi, j0, j1, k0, k1, threshold)
362 matmult(nil, A, B, C, mi, i1, j0, j1, k0, k1, threshold)
363 <-done1
364 } else if dj >= dk && dj >= threshold {
365 // divide in two by x axis
366 mj := j0 + dj/2
367 done1 := make(chan struct{}, 1)
368 go matmult(done1, A, B, C, i0, i1, j0, mj, k0, k1, threshold)
369 matmult(nil, A, B, C, i0, i1, mj, j1, k0, k1, threshold)
370 <-done1
371 } else if dk >= threshold {
372 // divide in two by "k" axis
373 // deliberately not parallel because of data races
374 mk := k0 + dk/2
375 matmult(nil, A, B, C, i0, i1, j0, j1, k0, mk, threshold)
376 matmult(nil, A, B, C, i0, i1, j0, j1, mk, k1, threshold)
377 } else {
378 // the matrices are small enough, compute directly
379 for i := i0; i < i1; i++ {
380 for j := j0; j < j1; j++ {
381 for k := k0; k < k1; k++ {
382 C[i][j] += A[i][k] * B[k][j]
383 }
384 }
385 }
386 }
387 if done != nil {
388 done <- struct{}{}
389 }
390}