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) interface{} 38 finalizer interface{} 39 }{ 40 {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }}, 41 {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }}, 42 {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }}, 43 {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }}, 44 {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }}, 45 } 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 73 type bigValue struct { 74 fill uint64 75 it bool 76 up string 77 } 78 79 func 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 88 runtime.SetFinalizer(v, func(v interface{}) { 89 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 110 func fin(v *int) { 111 } 112 113 // Verify we don't crash at least. golang.org/issue/6857 114 func TestFinalizerZeroSizedStruct(t *testing.T) { 115 type Z struct{} 116 z := new(Z) 117 runtime.SetFinalizer(z, func(*Z) {}) 118 } 119 120 func 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 138 func 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 151 const objsize = 320 152 153 type objtype [objsize]byte 154 155 func 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. 173 func TestEmptySlice(t *testing.T) { 174 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 190 var xsglobal []byte 191 192 func 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. 206 func 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 222 var ssglobal string 223 224 // Test for issue 7656. 225 func 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 232 type Object1 struct { 233 Something []byte 234 } 235 236 type Object2 struct { 237 Something byte 238 } 239 240 var ( 241 Foo2 = &Object2{} 242 Foo1 = &Object1{} 243 ) 244