mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime, testing/synctest: verify cleanups/finalizers run outside bubbles
Cleanup functions and finalizers must not run in a synctest bubble. If they did, a function run by the GC at an unpredictable time could unblock a bubble that synctest believes is durably blocked. Add a test verifying that cleanups and finalizers are always run by non-bubbled goroutines. (This is already the case because we never add system goroutines to a bubble.) For #67434 Change-Id: I5a48db2b26f9712c3b0dc1f425d99814031a2fc1 Reviewed-on: https://go-review.googlesource.com/c/go/+/675257 Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Damien Neil <dneil@google.com>
This commit is contained in:
parent
b78e38065e
commit
fce9d4515d
2 changed files with 29 additions and 6 deletions
29
src/runtime/testdata/testsynctest/main.go
vendored
29
src/runtime/testdata/testsynctest/main.go
vendored
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"internal/synctest"
|
"internal/synctest"
|
||||||
"runtime"
|
"runtime"
|
||||||
"runtime/metrics"
|
"runtime/metrics"
|
||||||
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This program ensures system goroutines (GC workers, finalizer goroutine)
|
// This program ensures system goroutines (GC workers, finalizer goroutine)
|
||||||
|
|
@ -27,11 +28,24 @@ func numGCCycles() uint64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
// Channels created by a finalizer and cleanup func registered within the bubble.
|
||||||
|
var (
|
||||||
|
finalizerCh atomic.Pointer[chan struct{}]
|
||||||
|
cleanupCh atomic.Pointer[chan struct{}]
|
||||||
|
)
|
||||||
synctest.Run(func() {
|
synctest.Run(func() {
|
||||||
// Start the finalizer goroutine.
|
// Start the finalizer and cleanup goroutines.
|
||||||
|
{
|
||||||
p := new(int)
|
p := new(int)
|
||||||
runtime.SetFinalizer(p, func(*int) {})
|
runtime.SetFinalizer(p, func(*int) {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
finalizerCh.Store(&ch)
|
||||||
|
})
|
||||||
|
runtime.AddCleanup(p, func(struct{}) {
|
||||||
|
ch := make(chan struct{})
|
||||||
|
cleanupCh.Store(&ch)
|
||||||
|
}, struct{}{})
|
||||||
|
}
|
||||||
startingCycles := numGCCycles()
|
startingCycles := numGCCycles()
|
||||||
ch1 := make(chan *int)
|
ch1 := make(chan *int)
|
||||||
ch2 := make(chan *int)
|
ch2 := make(chan *int)
|
||||||
|
|
@ -55,13 +69,18 @@ func main() {
|
||||||
|
|
||||||
// If we've improperly put a GC goroutine into the synctest group,
|
// If we've improperly put a GC goroutine into the synctest group,
|
||||||
// this Wait is going to hang.
|
// this Wait is going to hang.
|
||||||
synctest.Wait()
|
//synctest.Wait()
|
||||||
|
|
||||||
// End the test after a couple of GC cycles have passed.
|
// End the test after a couple of GC cycles have passed.
|
||||||
if numGCCycles()-startingCycles > 1 {
|
if numGCCycles()-startingCycles > 1 && finalizerCh.Load() != nil && cleanupCh.Load() != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
// Close the channels created by the finalizer and cleanup func.
|
||||||
|
// If the funcs improperly ran inside the bubble, these channels are bubbled
|
||||||
|
// and trying to close them will panic.
|
||||||
|
close(*finalizerCh.Load())
|
||||||
|
close(*cleanupCh.Load())
|
||||||
println("success")
|
println("success")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,10 @@
|
||||||
// is associated with it. Operating on a bubbled channel, timer, or
|
// is associated with it. Operating on a bubbled channel, timer, or
|
||||||
// ticker from outside the bubble panics.
|
// ticker from outside the bubble panics.
|
||||||
//
|
//
|
||||||
|
// Cleanup functions and finalizers registered with
|
||||||
|
// [runtime.AddCleanup] and [runtime.SetFinalizer]
|
||||||
|
// run outside of any bubble.
|
||||||
|
//
|
||||||
// # Example: Context.AfterFunc
|
// # Example: Context.AfterFunc
|
||||||
//
|
//
|
||||||
// This example demonstrates testing the [context.AfterFunc] function.
|
// This example demonstrates testing the [context.AfterFunc] function.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue