1// run
2
3// Copyright 2014 The Go Authors. All rights reserved.
4// Use of this source code is governed by a BSD-style
5// license that can be found in the LICENSE file.
6
7// Scenario that used to leak arbitrarily many SudoG structs.
8// See golang.org/issue/9110.
9
10package main
11
12import (
13	"runtime"
14	"runtime/debug"
15	"sync"
16	"time"
17)
18
19func main() {
20	runtime.GOMAXPROCS(1)
21	debug.SetGCPercent(1000000) // only GC when we ask for GC
22
23	var stats, stats1, stats2 runtime.MemStats
24
25	release := func() {}
26	for i := 0; i < 20; i++ {
27		if i == 10 {
28			// Should be warmed up by now.
29			runtime.ReadMemStats(&stats1)
30		}
31
32		c := make(chan int)
33		for i := 0; i < 10; i++ {
34			go func() {
35				select {
36				case <-c:
37				case <-c:
38				case <-c:
39				}
40			}()
41		}
42		time.Sleep(1 * time.Millisecond)
43		release()
44
45		close(c) // let select put its sudog's into the cache
46		time.Sleep(1 * time.Millisecond)
47
48		// pick up top sudog
49		var cond1 sync.Cond
50		var mu1 sync.Mutex
51		cond1.L = &mu1
52		go func() {
53			mu1.Lock()
54			cond1.Wait()
55			mu1.Unlock()
56		}()
57		time.Sleep(1 * time.Millisecond)
58
59		// pick up next sudog
60		var cond2 sync.Cond
61		var mu2 sync.Mutex
62		cond2.L = &mu2
63		go func() {
64			mu2.Lock()
65			cond2.Wait()
66			mu2.Unlock()
67		}()
68		time.Sleep(1 * time.Millisecond)
69
70		// put top sudog back
71		cond1.Broadcast()
72		time.Sleep(1 * time.Millisecond)
73
74		// drop cache on floor
75		runtime.GC()
76
77		// release cond2 after select has gotten to run
78		release = func() {
79			cond2.Broadcast()
80			time.Sleep(1 * time.Millisecond)
81		}
82	}
83
84	runtime.GC()
85
86	runtime.ReadMemStats(&stats2)
87
88	if int(stats2.HeapObjects)-int(stats1.HeapObjects) > 20 { // normally at most 1 or 2; was 300 with leak
89		print("BUG: object leak: ", stats.HeapObjects, " -> ", stats1.HeapObjects, " -> ", stats2.HeapObjects, "\n")
90	}
91}
92