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