github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/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 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 // Test case for argument spill slot. 46 // If the spill slot was not counted for the frame size, it will (incorrectly) choose 47 // call32 as the result has (exactly) 32 bytes. When the argument actually spills, 48 // it clobbers the caller's frame (likely the return PC). 49 {func(x *int) any { return x }, func(v any) [4]int64 { 50 print() // force spill 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 // allocate struct with pointer to avoid hitting tinyalloc. 60 // Otherwise we can't be sure when the allocation will 61 // be freed. 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 // Verify we don't crash at least. golang.org/issue/6857 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 // One chunk must be exactly one sizeclass in size. 157 // It should be a sizeclass not used much by others, so we 158 // have a greater chance of finding adjacent ones. 159 // size class 19: 320 byte objects, 25 per page, 1 page alloc at a time 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 // Make sure an empty slice on the stack doesn't pin the next object in memory. 182 func TestEmptySlice(t *testing.T) { 183 x, y := adjChunks() 184 185 // the pointer inside xs points to y. 186 xs := x[objsize:] // change objsize to objsize-1 and the test passes 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 // keep empty slice alive until here 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 // Make sure an empty string on the stack doesn't pin the next object in memory. 215 func TestEmptyString(t *testing.T) { 216 x, y := adjStringChunk() 217 218 ss := x[objsize:] // change objsize to objsize-1 and the test passes 219 fin := make(chan bool, 1) 220 // set finalizer on string contents of y 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 // keep 0-length string live until here 229 } 230 231 var ssglobal string 232 233 // Test for issue 7656. 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 // See issue 21402. 260 t.Parallel() 261 type T *int // needs to be a pointer base type to avoid tinyalloc and its never-finalized behavior. 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 }