github.com/hbdrawn/golang@v0.0.0-20141214014649-6b835209aba2/test/fixedbugs/issue9110.go (about)

     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  	debug.SetGCPercent(1000000) // only GC when we ask for GC
    21  
    22  	var stats, stats1, stats2 runtime.MemStats
    23  
    24  	release := func() {}
    25  	for i := 0; i < 20; i++ {
    26  		if i == 10 {
    27  			// Should be warmed up by now.
    28  			runtime.ReadMemStats(&stats1)
    29  		}
    30  
    31  		c := make(chan int)
    32  		for i := 0; i < 10; i++ {
    33  			go func() {
    34  				select {
    35  				case <-c:
    36  				case <-c:
    37  				case <-c:
    38  				}
    39  			}()
    40  		}
    41  		time.Sleep(1 * time.Millisecond)
    42  		release()
    43  
    44  		close(c) // let select put its sudog's into the cache
    45  		time.Sleep(1 * time.Millisecond)
    46  
    47  		// pick up top sudog
    48  		var cond1 sync.Cond
    49  		var mu1 sync.Mutex
    50  		cond1.L = &mu1
    51  		go func() {
    52  			mu1.Lock()
    53  			cond1.Wait()
    54  			mu1.Unlock()
    55  		}()
    56  		time.Sleep(1 * time.Millisecond)
    57  
    58  		// pick up next sudog
    59  		var cond2 sync.Cond
    60  		var mu2 sync.Mutex
    61  		cond2.L = &mu2
    62  		go func() {
    63  			mu2.Lock()
    64  			cond2.Wait()
    65  			mu2.Unlock()
    66  		}()
    67  		time.Sleep(1 * time.Millisecond)
    68  
    69  		// put top sudog back
    70  		cond1.Broadcast()
    71  		time.Sleep(1 * time.Millisecond)
    72  
    73  		// drop cache on floor
    74  		runtime.GC()
    75  
    76  		// release cond2 after select has gotten to run
    77  		release = func() {
    78  			cond2.Broadcast()
    79  			time.Sleep(1 * time.Millisecond)
    80  		}
    81  	}
    82  
    83  	runtime.GC()
    84  
    85  	runtime.ReadMemStats(&stats2)
    86  
    87  	if int(stats2.HeapObjects)-int(stats1.HeapObjects) > 20 { // normally at most 1 or 2; was 300 with leak
    88  		print("BUG: object leak: ", stats.HeapObjects, " -> ", stats1.HeapObjects, " -> ", stats2.HeapObjects, "\n")
    89  	}
    90  }