aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/testdata
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2024-06-11 11:02:18 -0700
committerDamien Neil <dneil@google.com>2024-11-19 19:40:40 +0000
commitd90ce588eac7b9105c0ca556a7c6e975fd5c1eca (patch)
tree4692a7f87738058c89bba874fe6d53b82786c44a /src/runtime/testdata
parent944df9a7516021f0405cd8adb1e6894ae9872cb5 (diff)
downloadgo-d90ce588eac7b9105c0ca556a7c6e975fd5c1eca.tar.xz
internal/synctest: new package for testing concurrent code
Add an internal (for now) implementation of testing/synctest. The synctest.Run function executes a tree of goroutines in an isolated environment using a fake clock. The synctest.Wait function allows a test to wait for all other goroutines within the test to reach a blocking point. For #67434 For #69687 Change-Id: Icb39e54c54cece96517e58ef9cfb18bf68506cfc Reviewed-on: https://go-review.googlesource.com/c/go/+/591997 Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/runtime/testdata')
-rw-r--r--src/runtime/testdata/testsynctest/main.go67
1 files changed, 67 insertions, 0 deletions
diff --git a/src/runtime/testdata/testsynctest/main.go b/src/runtime/testdata/testsynctest/main.go
new file mode 100644
index 0000000000..d2cbc99258
--- /dev/null
+++ b/src/runtime/testdata/testsynctest/main.go
@@ -0,0 +1,67 @@
+// Copyright 2024 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 (
+ "internal/synctest"
+ "runtime"
+ "runtime/metrics"
+)
+
+// This program ensures system goroutines (GC workers, finalizer goroutine)
+// started from within a synctest bubble do not participate in that bubble.
+//
+// To ensure none of these goroutines start before synctest.Run,
+// it must have no dependencies on packages which may start system goroutines.
+// This includes the os package, which creates finalizers at init time.
+
+func numGCCycles() uint64 {
+ samples := []metrics.Sample{{Name: "/gc/cycles/total:gc-cycles"}}
+ metrics.Read(samples)
+ if samples[0].Value.Kind() == metrics.KindBad {
+ panic("metric not supported")
+ }
+ return samples[0].Value.Uint64()
+}
+
+func main() {
+ synctest.Run(func() {
+ // Start the finalizer goroutine.
+ p := new(int)
+ runtime.SetFinalizer(p, func(*int) {})
+
+ startingCycles := numGCCycles()
+ ch1 := make(chan *int)
+ ch2 := make(chan *int)
+ defer close(ch1)
+ go func() {
+ for i := range ch1 {
+ v := *i + 1
+ ch2 <- &v
+ }
+ }()
+ for {
+ // Make a lot of short-lived allocations to get the GC working.
+ for i := 0; i < 1000; i++ {
+ v := new(int)
+ *v = i
+ // Set finalizers on these values, just for added stress.
+ runtime.SetFinalizer(v, func(*int) {})
+ ch1 <- v
+ <-ch2
+ }
+
+ // If we've improperly put a GC goroutine into the synctest group,
+ // this Wait is going to hang.
+ synctest.Wait()
+
+ // End the test after a couple of GC cycles have passed.
+ if numGCCycles()-startingCycles > 1 {
+ break
+ }
+ }
+ })
+ println("success")
+}