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 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 if runtime.Compiler == "gccgo" { 28 t.Skip("skipping for gccgo") 29 } 30 31 ch := make(chan bool, 10) 32 finalize := func(x *int) { 33 if *x != 97531 { 34 t.Errorf("finalizer %d, want %d", *x, 97531) 35 } 36 ch <- true 37 } 38 39 var finalizerTests = []struct { 40 convert func(*int) interface{} 41 finalizer interface{} 42 }{ 43 {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }}, 44 {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }}, 45 {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }}, 46 {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }}, 47 {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }}, 48 } 49 50 for i, tt := range finalizerTests { 51 done := make(chan bool, 1) 52 go func() { 53 // allocate struct with pointer to avoid hitting tinyalloc. 54 // Otherwise we can't be sure when the allocation will 55 // be freed. 56 type T struct { 57 v int 58 p unsafe.Pointer 59 } 60 v := &new(T).v 61 *v = 97531 62 runtime.SetFinalizer(tt.convert(v), tt.finalizer) 63 v = nil 64 done <- true 65 }() 66 <-done 67 runtime.GC() 68 select { 69 case <-ch: 70 case <-time.After(time.Second * 4): 71 t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer) 72 } 73 } 74} 75 76type bigValue struct { 77 fill uint64 78 it bool 79 up string 80} 81 82func TestFinalizerInterfaceBig(t *testing.T) { 83 if runtime.GOARCH != "amd64" { 84 t.Skipf("Skipping on non-amd64 machine") 85 } 86 if runtime.Compiler == "gccgo" { 87 t.Skip("skipping for gccgo") 88 } 89 ch := make(chan bool) 90 done := make(chan bool, 1) 91 go func() { 92 v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"} 93 old := *v 94 runtime.SetFinalizer(v, func(v interface{}) { 95 i, ok := v.(*bigValue) 96 if !ok { 97 t.Errorf("finalizer called with type %T, want *bigValue", v) 98 } 99 if *i != old { 100 t.Errorf("finalizer called with %+v, want %+v", *i, old) 101 } 102 close(ch) 103 }) 104 v = nil 105 done <- true 106 }() 107 <-done 108 runtime.GC() 109 select { 110 case <-ch: 111 case <-time.After(4 * time.Second): 112 t.Errorf("finalizer for type *bigValue didn't run") 113 } 114} 115 116func fin(v *int) { 117} 118 119// Verify we don't crash at least. golang.org/issue/6857 120func TestFinalizerZeroSizedStruct(t *testing.T) { 121 type Z struct{} 122 z := new(Z) 123 runtime.SetFinalizer(z, func(*Z) {}) 124} 125 126func BenchmarkFinalizer(b *testing.B) { 127 const Batch = 1000 128 b.RunParallel(func(pb *testing.PB) { 129 var data [Batch]*int 130 for i := 0; i < Batch; i++ { 131 data[i] = new(int) 132 } 133 for pb.Next() { 134 for i := 0; i < Batch; i++ { 135 runtime.SetFinalizer(data[i], fin) 136 } 137 for i := 0; i < Batch; i++ { 138 runtime.SetFinalizer(data[i], nil) 139 } 140 } 141 }) 142} 143 144func BenchmarkFinalizerRun(b *testing.B) { 145 b.RunParallel(func(pb *testing.PB) { 146 for pb.Next() { 147 v := new(int) 148 runtime.SetFinalizer(v, fin) 149 } 150 }) 151} 152 153// One chunk must be exactly one sizeclass in size. 154// It should be a sizeclass not used much by others, so we 155// have a greater chance of finding adjacent ones. 156// size class 19: 320 byte objects, 25 per page, 1 page alloc at a time 157const objsize = 320 158 159type objtype [objsize]byte 160 161func adjChunks() (*objtype, *objtype) { 162 var s []*objtype 163 164 for { 165 c := new(objtype) 166 for _, d := range s { 167 if uintptr(unsafe.Pointer(c))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(d)) { 168 return c, d 169 } 170 if uintptr(unsafe.Pointer(d))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(c)) { 171 return d, c 172 } 173 } 174 s = append(s, c) 175 } 176} 177 178// Make sure an empty slice on the stack doesn't pin the next object in memory. 179func TestEmptySlice(t *testing.T) { 180 if runtime.Compiler == "gccgo" { 181 t.Skip("skipping for gccgo") 182 } 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 199var xsglobal []byte 200 201func 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. 215func TestEmptyString(t *testing.T) { 216 if runtime.Compiler == "gccgo" { 217 t.Skip("skipping for gccgo") 218 } 219 220 x, y := adjStringChunk() 221 222 ss := x[objsize:] // change objsize to objsize-1 and the test passes 223 fin := make(chan bool, 1) 224 // set finalizer on string contents of y 225 runtime.SetFinalizer(y, func(z *objtype) { fin <- true }) 226 runtime.GC() 227 select { 228 case <-fin: 229 case <-time.After(4 * time.Second): 230 t.Errorf("finalizer of next string in memory didn't run") 231 } 232 ssglobal = ss // keep 0-length string live until here 233} 234 235var ssglobal string 236 237// Test for issue 7656. 238func TestFinalizerOnGlobal(t *testing.T) { 239 runtime.SetFinalizer(Foo1, func(p *Object1) {}) 240 runtime.SetFinalizer(Foo2, func(p *Object2) {}) 241 runtime.SetFinalizer(Foo1, nil) 242 runtime.SetFinalizer(Foo2, nil) 243} 244 245type Object1 struct { 246 Something []byte 247} 248 249type Object2 struct { 250 Something byte 251} 252 253var ( 254 Foo2 = &Object2{} 255 Foo1 = &Object1{} 256) 257 258func TestDeferKeepAlive(t *testing.T) { 259 if *flagQuick { 260 t.Skip("-quick") 261 } 262 263 // See issue 21402. 264 t.Parallel() 265 type T *int // needs to be a pointer base type to avoid tinyalloc and its never-finalized behavior. 266 x := new(T) 267 finRun := false 268 runtime.SetFinalizer(x, func(x *T) { 269 finRun = true 270 }) 271 defer runtime.KeepAlive(x) 272 runtime.GC() 273 time.Sleep(time.Second) 274 if finRun { 275 t.Errorf("finalizer ran prematurely") 276 } 277} 278