Source file src/runtime/testdata/testgoroutineleakprofile/goker/grpc3017.go

     1  // Copyright 2025 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a MIT
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"os"
     9  	"runtime"
    10  	"runtime/pprof"
    11  	"sync"
    12  	"time"
    13  )
    14  
    15  // This test case is a reproduction of grpc/3017.
    16  //
    17  // It is a goroutine leak that also simultaneously engages many GC assists.
    18  // Testing runtime behaviour when pivoting between regular and goroutine leak detection modes.
    19  
    20  func init() {
    21  	register("Grpc3017", Grpc3017)
    22  }
    23  
    24  type Address_grpc3017 int
    25  type SubConn_grpc3017 int
    26  
    27  type subConnCacheEntry_grpc3017 struct {
    28  	sc            SubConn_grpc3017
    29  	cancel        func()
    30  	abortDeleting bool
    31  }
    32  
    33  type lbCacheClientConn_grpc3017 struct {
    34  	mu            sync.Mutex // L1
    35  	timeout       time.Duration
    36  	subConnCache  map[Address_grpc3017]*subConnCacheEntry_grpc3017
    37  	subConnToAddr map[SubConn_grpc3017]Address_grpc3017
    38  }
    39  
    40  func (ccc *lbCacheClientConn_grpc3017) NewSubConn(addrs []Address_grpc3017) SubConn_grpc3017 {
    41  	if len(addrs) != 1 {
    42  		return SubConn_grpc3017(1)
    43  	}
    44  	addrWithoutMD := addrs[0]
    45  	ccc.mu.Lock() // L1
    46  	defer ccc.mu.Unlock()
    47  	if entry, ok := ccc.subConnCache[addrWithoutMD]; ok {
    48  		entry.cancel()
    49  		delete(ccc.subConnCache, addrWithoutMD)
    50  		return entry.sc
    51  	}
    52  	scNew := SubConn_grpc3017(1)
    53  	ccc.subConnToAddr[scNew] = addrWithoutMD
    54  	return scNew
    55  }
    56  
    57  func (ccc *lbCacheClientConn_grpc3017) RemoveSubConn(sc SubConn_grpc3017) {
    58  	ccc.mu.Lock() // L1
    59  	defer ccc.mu.Unlock()
    60  	addr, ok := ccc.subConnToAddr[sc]
    61  	if !ok {
    62  		return
    63  	}
    64  
    65  	if entry, ok := ccc.subConnCache[addr]; ok {
    66  		if entry.sc != sc {
    67  			delete(ccc.subConnToAddr, sc)
    68  		}
    69  		return
    70  	}
    71  
    72  	entry := &subConnCacheEntry_grpc3017{
    73  		sc: sc,
    74  	}
    75  	ccc.subConnCache[addr] = entry
    76  
    77  	timer := time.AfterFunc(ccc.timeout, func() { // G3
    78  		runtime.Gosched()
    79  		ccc.mu.Lock() // L1
    80  		if entry.abortDeleting {
    81  			return // Missing unlock
    82  		}
    83  		delete(ccc.subConnToAddr, sc)
    84  		delete(ccc.subConnCache, addr)
    85  		ccc.mu.Unlock()
    86  	})
    87  
    88  	entry.cancel = func() {
    89  		if !timer.Stop() {
    90  			entry.abortDeleting = true
    91  		}
    92  	}
    93  }
    94  
    95  func Grpc3017() {
    96  	prof := pprof.Lookup("goroutineleak")
    97  	defer func() {
    98  		time.Sleep(100 * time.Millisecond)
    99  		prof.WriteTo(os.Stdout, 2)
   100  	}()
   101  
   102  	for i := 0; i < 100; i++ {
   103  		go func() { //G1
   104  			done := make(chan struct{})
   105  
   106  			ccc := &lbCacheClientConn_grpc3017{
   107  				timeout:       time.Nanosecond,
   108  				subConnCache:  make(map[Address_grpc3017]*subConnCacheEntry_grpc3017),
   109  				subConnToAddr: make(map[SubConn_grpc3017]Address_grpc3017),
   110  			}
   111  
   112  			sc := ccc.NewSubConn([]Address_grpc3017{Address_grpc3017(1)})
   113  			go func() { // G2
   114  				for i := 0; i < 10000; i++ {
   115  					ccc.RemoveSubConn(sc)
   116  					sc = ccc.NewSubConn([]Address_grpc3017{Address_grpc3017(1)})
   117  				}
   118  				close(done)
   119  			}()
   120  			<-done
   121  		}()
   122  	}
   123  }
   124  

View as plain text