Source file src/runtime/testdata/testsynctest/main.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"internal/synctest"
     9  	"runtime"
    10  	"runtime/metrics"
    11  	"sync/atomic"
    12  	"unsafe"
    13  )
    14  
    15  // This program ensures system goroutines (GC workers, finalizer goroutine)
    16  // started from within a synctest bubble do not participate in that bubble.
    17  //
    18  // To ensure none of these goroutines start before synctest.Run,
    19  // it must have no dependencies on packages which may start system goroutines.
    20  // This includes the os package, which creates finalizers at init time.
    21  
    22  func numGCCycles() uint64 {
    23  	samples := []metrics.Sample{{Name: "/gc/cycles/total:gc-cycles"}}
    24  	metrics.Read(samples)
    25  	if samples[0].Value.Kind() == metrics.KindBad {
    26  		panic("metric not supported")
    27  	}
    28  	return samples[0].Value.Uint64()
    29  }
    30  
    31  type T struct {
    32  	v int
    33  	p unsafe.Pointer
    34  }
    35  
    36  func main() {
    37  	// Channels created by a finalizer and cleanup func registered within the bubble.
    38  	var (
    39  		finalizerCh atomic.Pointer[chan struct{}]
    40  		cleanupCh   atomic.Pointer[chan struct{}]
    41  	)
    42  	synctest.Run(func() {
    43  		// Start the finalizer and cleanup goroutines.
    44  		{
    45  			p := new(T)
    46  			runtime.SetFinalizer(p, func(*T) {
    47  				ch := make(chan struct{})
    48  				finalizerCh.Store(&ch)
    49  			})
    50  			runtime.AddCleanup(p, func(struct{}) {
    51  				ch := make(chan struct{})
    52  				cleanupCh.Store(&ch)
    53  			}, struct{}{})
    54  		}
    55  		startingCycles := numGCCycles()
    56  		ch1 := make(chan *T)
    57  		ch2 := make(chan *T)
    58  		defer close(ch1)
    59  		go func() {
    60  			for range ch1 {
    61  				ch2 <- &T{}
    62  			}
    63  		}()
    64  		const iterations = 1000
    65  		for range iterations {
    66  			// Make a lot of short-lived allocations to get the GC working.
    67  			for range 1000 {
    68  				v := new(T)
    69  				// Set finalizers on these values, just for added stress.
    70  				runtime.SetFinalizer(v, func(*T) {})
    71  				ch1 <- v
    72  				<-ch2
    73  			}
    74  
    75  			// If we've improperly put a GC goroutine into the synctest group,
    76  			// this Wait is going to hang.
    77  			synctest.Wait()
    78  
    79  			// End the test after a couple of GC cycles have passed.
    80  			if numGCCycles()-startingCycles > 1 && finalizerCh.Load() != nil && cleanupCh.Load() != nil {
    81  				return
    82  			}
    83  		}
    84  		println("finalizers/cleanups failed to run after", iterations, "cycles")
    85  	})
    86  	// Close the channels created by the finalizer and cleanup func.
    87  	// If the funcs improperly ran inside the bubble, these channels are bubbled
    88  	// and trying to close them will panic.
    89  	close(*finalizerCh.Load())
    90  	close(*cleanupCh.Load())
    91  	println("success")
    92  }
    93  

View as plain text