blob: 04ba7a68309539ccc078ab4ee8929cc042be6118 [file] [log] [blame]
Colin Cross7bb052a2015-02-03 12:59:37 -08001// Copyright 2011 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
5package runtime_test
6
7import (
8 "runtime"
9 "testing"
10 "time"
11 "unsafe"
12)
13
14type Tintptr *int // assignable to *int
15type Tint int // *Tint implements Tinter, interface{}
16
17func (t *Tint) m() {}
18
19type Tinter interface {
20 m()
21}
22
23func 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 {
Dan Willemsen59ee7802021-12-15 01:08:25 -080037 convert func(*int) any
38 finalizer any
Colin Cross7bb052a2015-02-03 12:59:37 -080039 }{
Dan Willemsen59ee7802021-12-15 01:08:25 -080040 {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))) }},
Colin Cross7bb052a2015-02-03 12:59:37 -080045 }
46
47 for i, tt := range finalizerTests {
48 done := make(chan bool, 1)
49 go func() {
50 // allocate struct with pointer to avoid hitting tinyalloc.
51 // Otherwise we can't be sure when the allocation will
52 // be freed.
53 type T struct {
54 v int
55 p unsafe.Pointer
56 }
57 v := &new(T).v
58 *v = 97531
59 runtime.SetFinalizer(tt.convert(v), tt.finalizer)
60 v = nil
61 done <- true
62 }()
63 <-done
64 runtime.GC()
65 select {
66 case <-ch:
67 case <-time.After(time.Second * 4):
68 t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer)
69 }
70 }
71}
72
73type bigValue struct {
74 fill uint64
75 it bool
76 up string
77}
78
79func TestFinalizerInterfaceBig(t *testing.T) {
80 if runtime.GOARCH != "amd64" {
81 t.Skipf("Skipping on non-amd64 machine")
82 }
83 ch := make(chan bool)
84 done := make(chan bool, 1)
85 go func() {
86 v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
87 old := *v
Dan Willemsen59ee7802021-12-15 01:08:25 -080088 runtime.SetFinalizer(v, func(v any) {
Colin Cross7bb052a2015-02-03 12:59:37 -080089 i, ok := v.(*bigValue)
90 if !ok {
91 t.Errorf("finalizer called with type %T, want *bigValue", v)
92 }
93 if *i != old {
94 t.Errorf("finalizer called with %+v, want %+v", *i, old)
95 }
96 close(ch)
97 })
98 v = nil
99 done <- true
100 }()
101 <-done
102 runtime.GC()
103 select {
104 case <-ch:
105 case <-time.After(4 * time.Second):
106 t.Errorf("finalizer for type *bigValue didn't run")
107 }
108}
109
110func fin(v *int) {
111}
112
113// Verify we don't crash at least. golang.org/issue/6857
114func TestFinalizerZeroSizedStruct(t *testing.T) {
115 type Z struct{}
116 z := new(Z)
117 runtime.SetFinalizer(z, func(*Z) {})
118}
119
120func BenchmarkFinalizer(b *testing.B) {
121 const Batch = 1000
122 b.RunParallel(func(pb *testing.PB) {
123 var data [Batch]*int
124 for i := 0; i < Batch; i++ {
125 data[i] = new(int)
126 }
127 for pb.Next() {
128 for i := 0; i < Batch; i++ {
129 runtime.SetFinalizer(data[i], fin)
130 }
131 for i := 0; i < Batch; i++ {
132 runtime.SetFinalizer(data[i], nil)
133 }
134 }
135 })
136}
137
138func BenchmarkFinalizerRun(b *testing.B) {
139 b.RunParallel(func(pb *testing.PB) {
140 for pb.Next() {
141 v := new(int)
142 runtime.SetFinalizer(v, fin)
143 }
144 })
145}
146
147// One chunk must be exactly one sizeclass in size.
148// It should be a sizeclass not used much by others, so we
149// have a greater chance of finding adjacent ones.
150// size class 19: 320 byte objects, 25 per page, 1 page alloc at a time
151const objsize = 320
152
153type objtype [objsize]byte
154
155func adjChunks() (*objtype, *objtype) {
156 var s []*objtype
157
158 for {
159 c := new(objtype)
160 for _, d := range s {
161 if uintptr(unsafe.Pointer(c))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(d)) {
162 return c, d
163 }
164 if uintptr(unsafe.Pointer(d))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(c)) {
165 return d, c
166 }
167 }
168 s = append(s, c)
169 }
170}
171
172// Make sure an empty slice on the stack doesn't pin the next object in memory.
173func TestEmptySlice(t *testing.T) {
Colin Cross7bb052a2015-02-03 12:59:37 -0800174 x, y := adjChunks()
175
176 // the pointer inside xs points to y.
177 xs := x[objsize:] // change objsize to objsize-1 and the test passes
178
179 fin := make(chan bool, 1)
180 runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
181 runtime.GC()
182 select {
183 case <-fin:
184 case <-time.After(4 * time.Second):
185 t.Errorf("finalizer of next object in memory didn't run")
186 }
187 xsglobal = xs // keep empty slice alive until here
188}
189
190var xsglobal []byte
191
192func adjStringChunk() (string, *objtype) {
193 b := make([]byte, objsize)
194 for {
195 s := string(b)
196 t := new(objtype)
197 p := *(*uintptr)(unsafe.Pointer(&s))
198 q := uintptr(unsafe.Pointer(t))
199 if p+objsize == q {
200 return s, t
201 }
202 }
203}
204
205// Make sure an empty string on the stack doesn't pin the next object in memory.
206func TestEmptyString(t *testing.T) {
207 x, y := adjStringChunk()
208
209 ss := x[objsize:] // change objsize to objsize-1 and the test passes
210 fin := make(chan bool, 1)
211 // set finalizer on string contents of y
212 runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
213 runtime.GC()
214 select {
215 case <-fin:
216 case <-time.After(4 * time.Second):
217 t.Errorf("finalizer of next string in memory didn't run")
218 }
219 ssglobal = ss // keep 0-length string live until here
220}
221
222var ssglobal string
223
224// Test for issue 7656.
225func TestFinalizerOnGlobal(t *testing.T) {
226 runtime.SetFinalizer(Foo1, func(p *Object1) {})
227 runtime.SetFinalizer(Foo2, func(p *Object2) {})
228 runtime.SetFinalizer(Foo1, nil)
229 runtime.SetFinalizer(Foo2, nil)
230}
231
232type Object1 struct {
233 Something []byte
234}
235
236type Object2 struct {
237 Something byte
238}
239
240var (
241 Foo2 = &Object2{}
242 Foo1 = &Object1{}
243)
Dan Willemsene1b3b182018-02-27 19:36:27 -0800244
245func TestDeferKeepAlive(t *testing.T) {
246 if *flagQuick {
247 t.Skip("-quick")
248 }
249
250 // See issue 21402.
251 t.Parallel()
252 type T *int // needs to be a pointer base type to avoid tinyalloc and its never-finalized behavior.
253 x := new(T)
254 finRun := false
255 runtime.SetFinalizer(x, func(x *T) {
256 finRun = true
257 })
258 defer runtime.KeepAlive(x)
259 runtime.GC()
260 time.Sleep(time.Second)
261 if finRun {
262 t.Errorf("finalizer ran prematurely")
263 }
264}