aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go
diff options
context:
space:
mode:
authorCherry Mui <cherryyz@google.com>2025-10-03 10:11:20 -0400
committerCherry Mui <cherryyz@google.com>2025-10-03 10:11:21 -0400
commitfb1749a3fe6ac40c56127d8fcea2e8b13db820e8 (patch)
tree72dd38fc84286bf88311850a99e6becf2cbd99bf /src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go
parent703a5fbaad81f1285776bf6f2900506d3c751ea1 (diff)
parentadce7f196e6ac6d22e9bc851efea5f3ab650947c (diff)
downloadgo-fb1749a3fe6ac40c56127d8fcea2e8b13db820e8.tar.xz
[dev.simd] all: merge master (adce7f1) into dev.simd
Conflicts: - src/internal/goexperiment/flags.go - src/runtime/export_test.go Merge List: + 2025-10-03 adce7f196e cmd/link: support .def file with MSVC clang toolchain + 2025-10-03 d5b950399d cmd/cgo: fix unaligned arguments typedmemmove crash on iOS + 2025-10-02 53845004d6 net/http/httputil: deprecate ReverseProxy.Director + 2025-10-02 bbdff9e8e1 net/http: update bundled x/net/http2 and delete obsolete http2inTests + 2025-10-02 4008e07080 io/fs: move path name documentation up to the package doc comment + 2025-10-02 0e4e2e6832 runtime: skip TestGoroutineLeakProfile under mayMoreStackPreempt + 2025-10-02 f03c392295 runtime: fix aix/ppc64 library initialization + 2025-10-02 707454b41f cmd/go: update `go help mod edit` with the tool and ignore sections + 2025-10-02 8c68a1c1ab runtime,net/http/pprof: goroutine leak detection by using the garbage collector + 2025-10-02 84db201ae1 cmd/compile: propagate len([]T{}) to make builtin to allow stack allocation + 2025-10-02 5799c139a7 crypto/tls: rm marshalEncryptedClientHelloConfigList dead code + 2025-10-01 633dd1d475 encoding/json: fix Decoder.InputOffset regression in goexperiment.jsonv2 + 2025-10-01 8ad27fb656 doc/go_spec.html: update date + 2025-10-01 3f451f2c54 testing/synctest: fix inverted test failure message in TestContextAfterFunc + 2025-10-01 be0fed8a5f cmd/go/testdata/script/test_fuzz_fuzztime.txt: disable + 2025-09-30 eb1c7f6e69 runtime: move loong64 library entry point to os-agnostic file + 2025-09-30 c9257151e5 runtime: unify ppc64/ppc64le library entry point + 2025-09-30 4ff8a457db test/codegen: codify handling of floating point constants on arm64 + 2025-09-30 fcb893fc4b cmd/compile/internal/ssa: remove redundant "type:" prefix check + 2025-09-30 19cc1022ba mime: reduce allocs incurred by ParseMediaType + 2025-09-30 08afc50bea mime: extend "builtinTypes" to include a more complete list of common types + 2025-09-30 97da068774 cmd/compile: eliminate nil checks on .dict arg + 2025-09-30 300d9d2714 runtime: initialise debug settings much earlier in startup process + 2025-09-30 a846bb0aa5 errors: add AsType + 2025-09-30 7c8166d02d cmd/link/internal/arm64: support Mach-O ARM64_RELOC_SUBTRACTOR in internal linking + 2025-09-30 6e95748335 cmd/link/internal/arm64: support Mach-O ARM64_RELOC_POINTER_TO_GOT in internal linking + 2025-09-30 742f92063e cmd/compile, runtime: always enable Wasm signext and satconv features + 2025-09-30 db10db6be3 internal/poll: remove operation fields from FD + 2025-09-29 75c87df58e internal/poll: pass the I/O mode instead of an overlapped object in execIO + 2025-09-29 fc88e18b4a crypto/internal/fips140/entropy: add CPU jitter-based entropy source + 2025-09-29 db4fade759 crypto/internal/fips140/mlkem: make CAST conditional + 2025-09-29 db3cb3fd9a runtime: correct reference to getStackMap in comment + 2025-09-29 690fc2fb05 internal/poll: remove buf field from operation + 2025-09-29 eaf2345256 cmd/link: use a .def file to mark exported symbols on Windows + 2025-09-29 4b77733565 internal/syscall/windows: regenerate GetFileSizeEx + 2025-09-29 4e9006a716 crypto/tls: quote protocols in ALPN error message + 2025-09-29 047c2ab841 cmd/link: don't pass -Wl,-S on Solaris + 2025-09-29 ae8eba071b cmd/link: use correct length for pcln.cutab + 2025-09-29 fe3ba74b9e cmd/link: skip TestFlagW on platforms without DWARF symbol table + 2025-09-29 d42d56b764 encoding/xml: make use of reflect.TypeAssert + 2025-09-29 6d51f93257 runtime: jump instead of branch in netbsd/arm64 entry point + 2025-09-28 5500cbf0e4 debug/elf: prevent offset overflow + 2025-09-27 34e67623a8 all: fix typos + 2025-09-27 af6999e60d cmd/compile: implement jump table on loong64 + 2025-09-26 63cd912083 os/user: simplify go:build + 2025-09-26 53009b26dd runtime: use a smaller arena size on Wasm + 2025-09-26 3a5df9d2b2 net/http: add HTTP2Config.StrictMaxConcurrentRequests + 2025-09-26 16be34df02 net/http: add more tests of transport connection pool + 2025-09-26 3e4540b49d os/user: use getgrouplist on illumos && cgo + 2025-09-26 15fbe3480b internal/poll: simplify WriteMsg and ReadMsg on Windows + 2025-09-26 16ae11a9e1 runtime: move TestReadMetricsSched to testprog + 2025-09-26 459f3a3adc cmd/link: don't pass -Wl,-S on AIX + 2025-09-26 4631a2d3c6 cmd/link: skip TestFlagW on AIX + 2025-09-26 0f31d742cd cmd/compile: fix ICE with new(<untyped expr>) + 2025-09-26 7d7cd6e07b internal/poll: don't call SetFilePointerEx in Seek for overlapped handles + 2025-09-26 41cba31e66 mime/multipart: percent-encode CR and LF in header values to avoid CRLF injection + 2025-09-26 dd1d597c3a Revert "cmd/internal/obj/loong64: use the MOVVP instruction to optimize prologue" + 2025-09-26 45d6bc76af runtime: unify arm64 entry point code + 2025-09-25 fdea7da3e6 runtime: use common library entry point on windows amd64/386 + 2025-09-25 e8a4f508d1 lib/fips140: re-seal v1.0.0 + 2025-09-25 9b7a328089 crypto/internal/fips140: remove key import PCTs, make keygen PCTs fatal + 2025-09-25 7f9ab7203f crypto/internal/fips140: update frozen module version to "v1.0.0" + 2025-09-25 fb5719cbda crypto/internal/fips140/ecdsa: make TestingOnlyNewDRBG generic + 2025-09-25 56067e31f2 std: remove unused declarations Change-Id: Iecb28fd62c69fbed59da557f46d31bae55889e2c
Diffstat (limited to 'src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go')
-rw-r--r--src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go277
1 files changed, 277 insertions, 0 deletions
diff --git a/src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go b/src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go
new file mode 100644
index 0000000000..353e48ee70
--- /dev/null
+++ b/src/runtime/testdata/testgoroutineleakprofile/commonpatterns.go
@@ -0,0 +1,277 @@
+// Copyright 2025 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.
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+ "runtime"
+ "runtime/pprof"
+ "time"
+)
+
+// Common goroutine leak patterns. Extracted from:
+// "Unveiling and Vanquishing Goroutine Leaks in Enterprise Microservices: A Dynamic Analysis Approach"
+// doi:10.1109/CGO57630.2024.10444835
+//
+// Tests in this file are not flaky iff. the test is run with GOMAXPROCS=1.
+// The main goroutine forcefully yields via `runtime.Gosched()` before
+// running the profiler. This moves them to the back of the run queue,
+// allowing the leaky goroutines to be scheduled beforehand and get stuck.
+
+func init() {
+ register("NoCloseRange", NoCloseRange)
+ register("MethodContractViolation", MethodContractViolation)
+ register("DoubleSend", DoubleSend)
+ register("EarlyReturn", EarlyReturn)
+ register("NCastLeak", NCastLeak)
+ register("Timeout", Timeout)
+}
+
+// Incoming list of items and the number of workers.
+func noCloseRange(list []any, workers int) {
+ ch := make(chan any)
+
+ // Create each worker
+ for i := 0; i < workers; i++ {
+ go func() {
+
+ // Each worker waits for an item and processes it.
+ for item := range ch {
+ // Process each item
+ _ = item
+ }
+ }()
+ }
+
+ // Send each item to one of the workers.
+ for _, item := range list {
+ // Sending can leak if workers == 0 or if one of the workers panics
+ ch <- item
+ }
+ // The channel is never closed, so workers leak once there are no more
+ // items left to process.
+}
+
+func NoCloseRange() {
+ prof := pprof.Lookup("goroutineleak")
+ defer func() {
+ time.Sleep(100 * time.Millisecond)
+ prof.WriteTo(os.Stdout, 2)
+ }()
+
+ go noCloseRange([]any{1, 2, 3}, 0)
+ go noCloseRange([]any{1, 2, 3}, 3)
+}
+
+// A worker processes items pushed to `ch` one by one in the background.
+// When the worker is no longer needed, it must be closed with `Stop`.
+//
+// Specifications:
+//
+// A worker may be started any number of times, but must be stopped only once.
+// Stopping a worker multiple times will lead to a close panic.
+// Any worker that is started must eventually be stopped.
+// Failing to stop a worker results in a goroutine leak
+type worker struct {
+ ch chan any
+ done chan any
+}
+
+// Start spawns a background goroutine that extracts items pushed to the queue.
+func (w worker) Start() {
+ go func() {
+
+ for {
+ select {
+ case <-w.ch: // Normal workflow
+ case <-w.done:
+ return // Shut down
+ }
+ }
+ }()
+}
+
+func (w worker) Stop() {
+ // Allows goroutine created by Start to terminate
+ close(w.done)
+}
+
+func (w worker) AddToQueue(item any) {
+ w.ch <- item
+}
+
+// worker limited in scope by workerLifecycle
+func workerLifecycle(items []any) {
+ // Create a new worker
+ w := worker{
+ ch: make(chan any),
+ done: make(chan any),
+ }
+ // Start worker
+ w.Start()
+
+ // Operate on worker
+ for _, item := range items {
+ w.AddToQueue(item)
+ }
+
+ runtime.Gosched()
+ // Exits without calling ’Stop’. Goroutine created by `Start` eventually leaks.
+}
+
+func MethodContractViolation() {
+ prof := pprof.Lookup("goroutineleak")
+ defer func() {
+ runtime.Gosched()
+ prof.WriteTo(os.Stdout, 2)
+ }()
+
+ workerLifecycle(make([]any, 10))
+ runtime.Gosched()
+}
+
+// doubleSend incoming channel must send a message (incoming error simulates an error generated internally).
+func doubleSend(ch chan any, err error) {
+ if err != nil {
+ // In case of an error, send nil.
+ ch <- nil
+ // Return is missing here.
+ }
+ // Otherwise, continue with normal behaviour
+ // This send is still executed in the error case, which may lead to a goroutine leak.
+ ch <- struct{}{}
+}
+
+func DoubleSend() {
+ prof := pprof.Lookup("goroutineleak")
+ ch := make(chan any)
+ defer func() {
+ runtime.Gosched()
+ prof.WriteTo(os.Stdout, 2)
+ }()
+
+ go func() {
+ doubleSend(ch, nil)
+ }()
+ <-ch
+
+ go func() {
+ doubleSend(ch, fmt.Errorf("error"))
+ }()
+ <-ch
+
+ ch1 := make(chan any, 1)
+ go func() {
+ doubleSend(ch1, fmt.Errorf("error"))
+ }()
+ <-ch1
+}
+
+// earlyReturn demonstrates a common pattern of goroutine leaks.
+// A return statement interrupts the evaluation of the parent goroutine before it can consume a message.
+// Incoming error simulates an error produced internally.
+func earlyReturn(err error) {
+ // Create a synchronous channel
+ ch := make(chan any)
+
+ go func() {
+
+ // Send something to the channel.
+ // Leaks if the parent goroutine terminates early.
+ ch <- struct{}{}
+ }()
+
+ if err != nil {
+ // Interrupt evaluation of parent early in case of error.
+ // Sender leaks.
+ return
+ }
+
+ // Only receive if there is no error.
+ <-ch
+}
+
+func EarlyReturn() {
+ prof := pprof.Lookup("goroutineleak")
+ defer func() {
+ runtime.Gosched()
+ prof.WriteTo(os.Stdout, 2)
+ }()
+
+ go earlyReturn(fmt.Errorf("error"))
+}
+
+// nCastLeak processes a number of items. First result to pass the post is retrieved from the channel queue.
+func nCastLeak(items []any) {
+ // Channel is synchronous.
+ ch := make(chan any)
+
+ // Iterate over every item
+ for range items {
+ go func() {
+
+ // Process item and send result to channel
+ ch <- struct{}{}
+ // Channel is synchronous: only one sender will synchronise
+ }()
+ }
+ // Retrieve first result. All other senders block.
+ // Receiver blocks if there are no senders.
+ <-ch
+}
+
+func NCastLeak() {
+ prof := pprof.Lookup("goroutineleak")
+ defer func() {
+ for i := 0; i < yieldCount; i++ {
+ // Yield enough times to allow all the leaky goroutines to
+ // reach the execution point.
+ runtime.Gosched()
+ }
+ prof.WriteTo(os.Stdout, 2)
+ }()
+
+ go func() {
+ nCastLeak(nil)
+ }()
+
+ go func() {
+ nCastLeak(make([]any, 5))
+ }()
+}
+
+// A context is provided to short-circuit evaluation, leading
+// the sender goroutine to leak.
+func timeout(ctx context.Context) {
+ ch := make(chan any)
+
+ go func() {
+ ch <- struct{}{}
+ }()
+
+ select {
+ case <-ch: // Receive message
+ // Sender is released
+ case <-ctx.Done(): // Context was cancelled or timed out
+ // Sender is leaked
+ }
+}
+
+func Timeout() {
+ prof := pprof.Lookup("goroutineleak")
+ defer func() {
+ runtime.Gosched()
+ prof.WriteTo(os.Stdout, 2)
+ }()
+
+ ctx, cancel := context.WithCancel(context.Background())
+ cancel()
+
+ for i := 0; i < 100; i++ {
+ go timeout(ctx)
+ }
+}