Source file
src/runtime/mfinal_test.go
1
2
3
4
5 package runtime_test
6
7 import (
8 "runtime"
9 "testing"
10 "time"
11 "unsafe"
12 )
13
14 type Tintptr *int
15 type Tint int
16
17 func (t *Tint) m() {}
18
19 type Tinter interface {
20 m()
21 }
22
23 func TestFinalizerType(t *testing.T) {
24 if runtime.GOARCH != "amd64" {
25 t.Skipf("Skipping on non-amd64 machine")
26 }
27
28 ch := make(chan bool, 10)
29 finalize := func(x *int) {
30 if *x != 97531 {
31 t.Errorf("finalizer %d, want %d", *x, 97531)
32 }
33 ch <- true
34 }
35
36 var finalizerTests = []struct {
37 convert func(*int) any
38 finalizer any
39 }{
40 {func(x *int) any { return x }, func(v *int) { finalize(v) }},
41 {func(x *int) any { return Tintptr(x) }, func(v Tintptr) { finalize(v) }},
42 {func(x *int) any { return Tintptr(x) }, func(v *int) { finalize(v) }},
43 {func(x *int) any { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
44 {func(x *int) any { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
45
46
47
48
49 {func(x *int) any { return x }, func(v any) [4]int64 {
50 print()
51 finalize(v.(*int))
52 return [4]int64{}
53 }},
54 }
55
56 for i, tt := range finalizerTests {
57 done := make(chan bool, 1)
58 go func() {
59
60
61
62 type T struct {
63 v int
64 p unsafe.Pointer
65 }
66 v := &new(T).v
67 *v = 97531
68 runtime.SetFinalizer(tt.convert(v), tt.finalizer)
69 v = nil
70 done <- true
71 }()
72 <-done
73 runtime.GC()
74 select {
75 case <-ch:
76 case <-time.After(time.Second * 4):
77 t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer)
78 }
79 }
80 }
81
82 type bigValue struct {
83 fill uint64
84 it bool
85 up string
86 }
87
88 func TestFinalizerInterfaceBig(t *testing.T) {
89 if runtime.GOARCH != "amd64" {
90 t.Skipf("Skipping on non-amd64 machine")
91 }
92 ch := make(chan bool)
93 done := make(chan bool, 1)
94 go func() {
95 v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
96 old := *v
97 runtime.SetFinalizer(v, func(v any) {
98 i, ok := v.(*bigValue)
99 if !ok {
100 t.Errorf("finalizer called with type %T, want *bigValue", v)
101 }
102 if *i != old {
103 t.Errorf("finalizer called with %+v, want %+v", *i, old)
104 }
105 close(ch)
106 })
107 v = nil
108 done <- true
109 }()
110 <-done
111 runtime.GC()
112 select {
113 case <-ch:
114 case <-time.After(4 * time.Second):
115 t.Errorf("finalizer for type *bigValue didn't run")
116 }
117 }
118
119 func fin(v *int) {
120 }
121
122
123 func TestFinalizerZeroSizedStruct(t *testing.T) {
124 type Z struct{}
125 z := new(Z)
126 runtime.SetFinalizer(z, func(*Z) {})
127 }
128
129 func BenchmarkFinalizer(b *testing.B) {
130 const Batch = 1000
131 b.RunParallel(func(pb *testing.PB) {
132 var data [Batch]*int
133 for i := 0; i < Batch; i++ {
134 data[i] = new(int)
135 }
136 for pb.Next() {
137 for i := 0; i < Batch; i++ {
138 runtime.SetFinalizer(data[i], fin)
139 }
140 for i := 0; i < Batch; i++ {
141 runtime.SetFinalizer(data[i], nil)
142 }
143 }
144 })
145 }
146
147 func BenchmarkFinalizerRun(b *testing.B) {
148 b.RunParallel(func(pb *testing.PB) {
149 for pb.Next() {
150 v := new(int)
151 runtime.SetFinalizer(v, fin)
152 }
153 })
154 }
155
156
157
158
159
160 const objsize = 320
161
162 type objtype [objsize]byte
163
164 func adjChunks() (*objtype, *objtype) {
165 var s []*objtype
166
167 for {
168 c := new(objtype)
169 for _, d := range s {
170 if uintptr(unsafe.Pointer(c))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(d)) {
171 return c, d
172 }
173 if uintptr(unsafe.Pointer(d))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(c)) {
174 return d, c
175 }
176 }
177 s = append(s, c)
178 }
179 }
180
181
182 func TestEmptySlice(t *testing.T) {
183 x, y := adjChunks()
184
185
186 xs := x[objsize:]
187
188 fin := make(chan bool, 1)
189 runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
190 runtime.GC()
191 select {
192 case <-fin:
193 case <-time.After(4 * time.Second):
194 t.Errorf("finalizer of next object in memory didn't run")
195 }
196 xsglobal = xs
197 }
198
199 var xsglobal []byte
200
201 func adjStringChunk() (string, *objtype) {
202 b := make([]byte, objsize)
203 for {
204 s := string(b)
205 t := new(objtype)
206 p := *(*uintptr)(unsafe.Pointer(&s))
207 q := uintptr(unsafe.Pointer(t))
208 if p+objsize == q {
209 return s, t
210 }
211 }
212 }
213
214
215 func TestEmptyString(t *testing.T) {
216 x, y := adjStringChunk()
217
218 ss := x[objsize:]
219 fin := make(chan bool, 1)
220
221 runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
222 runtime.GC()
223 select {
224 case <-fin:
225 case <-time.After(4 * time.Second):
226 t.Errorf("finalizer of next string in memory didn't run")
227 }
228 ssglobal = ss
229 }
230
231 var ssglobal string
232
233
234 func TestFinalizerOnGlobal(t *testing.T) {
235 runtime.SetFinalizer(Foo1, func(p *Object1) {})
236 runtime.SetFinalizer(Foo2, func(p *Object2) {})
237 runtime.SetFinalizer(Foo1, nil)
238 runtime.SetFinalizer(Foo2, nil)
239 }
240
241 type Object1 struct {
242 Something []byte
243 }
244
245 type Object2 struct {
246 Something byte
247 }
248
249 var (
250 Foo2 = &Object2{}
251 Foo1 = &Object1{}
252 )
253
254 func TestDeferKeepAlive(t *testing.T) {
255 if *flagQuick {
256 t.Skip("-quick")
257 }
258
259
260 t.Parallel()
261 type T *int
262 x := new(T)
263 finRun := false
264 runtime.SetFinalizer(x, func(x *T) {
265 finRun = true
266 })
267 defer runtime.KeepAlive(x)
268 runtime.GC()
269 time.Sleep(time.Second)
270 if finRun {
271 t.Errorf("finalizer ran prematurely")
272 }
273 }
274
View as plain text