1
2
3
4
5 package main
6
7 import (
8 "internal/synctest"
9 "runtime"
10 "runtime/metrics"
11 "sync/atomic"
12 "unsafe"
13 )
14
15
16
17
18
19
20
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
38 var (
39 finalizerCh atomic.Pointer[chan struct{}]
40 cleanupCh atomic.Pointer[chan struct{}]
41 )
42 synctest.Run(func() {
43
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
67 for range 1000 {
68 v := new(T)
69
70 runtime.SetFinalizer(v, func(*T) {})
71 ch1 <- v
72 <-ch2
73 }
74
75
76
77 synctest.Wait()
78
79
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
87
88
89 close(*finalizerCh.Load())
90 close(*cleanupCh.Load())
91 println("success")
92 }
93
View as plain text