github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/runtime/mfinal_test.go (about) 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 }