modernc.org/gc@v1.0.1-0.20240304020402-f0dba7c97c2b/testdata/errchk/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  	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  }