// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a MIT // license that can be found in the LICENSE file. /* * Project: etcd * Issue or PR : https://github.com/etcd-io/etcd/commit/7618fdd1d642e47cac70c03f637b0fd798a53a6e * Buggy version: 377f19b0031f9c0aafe2aec28b6f9019311f52f9 * fix commit-id: 7618fdd1d642e47cac70c03f637b0fd798a53a6e * Flaky: 9/100 */ package main import ( "os" "runtime/pprof" "sync" "time" ) func init() { register("Etcd6873", Etcd6873) } type watchBroadcast_etcd6873 struct{} type watchBroadcasts_etcd6873 struct { mu sync.Mutex updatec chan *watchBroadcast_etcd6873 donec chan struct{} } func newWatchBroadcasts_etcd6873() *watchBroadcasts_etcd6873 { wbs := &watchBroadcasts_etcd6873{ updatec: make(chan *watchBroadcast_etcd6873, 1), donec: make(chan struct{}), } go func() { // G2 defer close(wbs.donec) for wb := range wbs.updatec { wbs.coalesce(wb) } }() return wbs } func (wbs *watchBroadcasts_etcd6873) coalesce(wb *watchBroadcast_etcd6873) { wbs.mu.Lock() wbs.mu.Unlock() } func (wbs *watchBroadcasts_etcd6873) stop() { wbs.mu.Lock() defer wbs.mu.Unlock() close(wbs.updatec) <-wbs.donec } func (wbs *watchBroadcasts_etcd6873) update(wb *watchBroadcast_etcd6873) { select { case wbs.updatec <- wb: default: } } // Example of goroutine leak trace: // // G1 G2 G3 //--------------------------------------------------------- // newWatchBroadcasts() // wbs.update() // wbs.updatec <- // return // <-wbs.updatec // wbs.coalesce() // wbs.stop() // wbs.mu.Lock() // close(wbs.updatec) // <-wbs.donec // wbs.mu.Lock() //---------------------G2,G3 leak------------------------- // func Etcd6873() { prof := pprof.Lookup("goroutineleak") defer func() { time.Sleep(100 * time.Millisecond) prof.WriteTo(os.Stdout, 2) }() for i := 0; i < 100; i++ { go func() { wbs := newWatchBroadcasts_etcd6873() // G1 wbs.update(&watchBroadcast_etcd6873{}) go wbs.stop() // G3 }() } }