// run // Copyright 2014 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Scenario that used to leak arbitrarily many SudoG structs. // See golang.org/issue/9110. package main import ( "runtime" "runtime/debug" "sync" "time" ) func main() { runtime.GOMAXPROCS(1) debug.SetGCPercent(1000000) // only GC when we ask for GC var stats, stats1, stats2 runtime.MemStats release := func() {} for i := 0; i < 20; i++ { if i == 10 { // Should be warmed up by now. runtime.ReadMemStats(&stats1) } c := make(chan int) for i := 0; i < 10; i++ { go func() { select { case <-c: case <-c: case <-c: } }() } time.Sleep(1 * time.Millisecond) release() close(c) // let select put its sudog's into the cache time.Sleep(1 * time.Millisecond) // pick up top sudog var cond1 sync.Cond var mu1 sync.Mutex cond1.L = &mu1 go func() { mu1.Lock() cond1.Wait() mu1.Unlock() }() time.Sleep(1 * time.Millisecond) // pick up next sudog var cond2 sync.Cond var mu2 sync.Mutex cond2.L = &mu2 go func() { mu2.Lock() cond2.Wait() mu2.Unlock() }() time.Sleep(1 * time.Millisecond) // put top sudog back cond1.Broadcast() time.Sleep(1 * time.Millisecond) // drop cache on floor runtime.GC() // release cond2 after select has gotten to run release = func() { cond2.Broadcast() time.Sleep(1 * time.Millisecond) } } runtime.GC() runtime.ReadMemStats(&stats2) if int(stats2.HeapObjects)-int(stats1.HeapObjects) > 20 { // normally at most 1 or 2; was 300 with leak print("BUG: object leak: ", stats.HeapObjects, " -> ", stats1.HeapObjects, " -> ", stats2.HeapObjects, "\n") } }