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 10 package main 11 12 import ( 13 "runtime" 14 "runtime/debug" 15 "sync" 16 "time" 17 ) 18 19 func 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