Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 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" |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 9 | "testing" |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 10 | "time" |
Keith Randall | 1b45cc4 | 2014-03-25 14:11:34 -0700 | [diff] [blame] | 11 | "unsafe" |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 12 | ) |
| 13 | |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 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() |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 21 | } |
| 22 | |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 23 | func TestFinalizerType(t *testing.T) { |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 24 | if runtime.GOARCH != "amd64" { |
| 25 | t.Skipf("Skipping on non-amd64 machine") |
| 26 | } |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 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 | |
Russ Cox | 9a5b055 | 2014-10-06 14:18:09 -0400 | [diff] [blame] | 47 | for i, tt := range finalizerTests { |
Rémy Oudompheng | e6b0234 | 2013-12-19 21:37:44 +0100 | [diff] [blame] | 48 | done := make(chan bool, 1) |
Russ Cox | 5f853d7 | 2013-10-02 12:30:49 -0400 | [diff] [blame] | 49 | go func() { |
Russ Cox | 9a5b055 | 2014-10-06 14:18:09 -0400 | [diff] [blame] | 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 |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 58 | *v = 97531 |
| 59 | runtime.SetFinalizer(tt.convert(v), tt.finalizer) |
| 60 | v = nil |
Rémy Oudompheng | e6b0234 | 2013-12-19 21:37:44 +0100 | [diff] [blame] | 61 | done <- true |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 62 | }() |
Rémy Oudompheng | e6b0234 | 2013-12-19 21:37:44 +0100 | [diff] [blame] | 63 | <-done |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 64 | runtime.GC() |
| 65 | select { |
| 66 | case <-ch: |
| 67 | case <-time.After(time.Second * 4): |
Russ Cox | 9a5b055 | 2014-10-06 14:18:09 -0400 | [diff] [blame] | 68 | t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer) |
Russ Cox | 5822e78 | 2013-08-14 14:54:31 -0400 | [diff] [blame] | 69 | } |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 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) |
Rémy Oudompheng | e6b0234 | 2013-12-19 21:37:44 +0100 | [diff] [blame] | 84 | done := make(chan bool, 1) |
Russ Cox | 5f853d7 | 2013-10-02 12:30:49 -0400 | [diff] [blame] | 85 | go func() { |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 86 | v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"} |
Russ Cox | 5f853d7 | 2013-10-02 12:30:49 -0400 | [diff] [blame] | 87 | old := *v |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 88 | runtime.SetFinalizer(v, func(v interface{}) { |
| 89 | i, ok := v.(*bigValue) |
| 90 | if !ok { |
Russ Cox | 5f853d7 | 2013-10-02 12:30:49 -0400 | [diff] [blame] | 91 | t.Errorf("finalizer called with type %T, want *bigValue", v) |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 92 | } |
Russ Cox | 5f853d7 | 2013-10-02 12:30:49 -0400 | [diff] [blame] | 93 | if *i != old { |
| 94 | t.Errorf("finalizer called with %+v, want %+v", *i, old) |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 95 | } |
| 96 | close(ch) |
| 97 | }) |
| 98 | v = nil |
Rémy Oudompheng | e6b0234 | 2013-12-19 21:37:44 +0100 | [diff] [blame] | 99 | done <- true |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 100 | }() |
Rémy Oudompheng | e6b0234 | 2013-12-19 21:37:44 +0100 | [diff] [blame] | 101 | <-done |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 102 | runtime.GC() |
| 103 | select { |
| 104 | case <-ch: |
Russ Cox | 5f853d7 | 2013-10-02 12:30:49 -0400 | [diff] [blame] | 105 | case <-time.After(4 * time.Second): |
| 106 | t.Errorf("finalizer for type *bigValue didn't run") |
Pieter Droogendijk | 6350e45 | 2013-07-29 19:43:08 +0400 | [diff] [blame] | 107 | } |
| 108 | } |
| 109 | |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 110 | func fin(v *int) { |
| 111 | } |
| 112 | |
Brad Fitzpatrick | 4b76a31 | 2013-12-17 14:18:58 -0800 | [diff] [blame] | 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 | |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 120 | func BenchmarkFinalizer(b *testing.B) { |
Dmitriy Vyukov | 69257d1 | 2014-02-24 20:50:12 +0400 | [diff] [blame] | 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) |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 130 | } |
Dmitriy Vyukov | 69257d1 | 2014-02-24 20:50:12 +0400 | [diff] [blame] | 131 | for i := 0; i < Batch; i++ { |
| 132 | runtime.SetFinalizer(data[i], nil) |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 133 | } |
Dmitriy Vyukov | 69257d1 | 2014-02-24 20:50:12 +0400 | [diff] [blame] | 134 | } |
| 135 | }) |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | func BenchmarkFinalizerRun(b *testing.B) { |
Dmitriy Vyukov | 69257d1 | 2014-02-24 20:50:12 +0400 | [diff] [blame] | 139 | b.RunParallel(func(pb *testing.PB) { |
| 140 | for pb.Next() { |
| 141 | v := new(int) |
| 142 | runtime.SetFinalizer(v, fin) |
| 143 | } |
| 144 | }) |
Dmitriy Vyukov | c14b268 | 2011-10-06 18:42:51 +0300 | [diff] [blame] | 145 | } |
Keith Randall | 1b45cc4 | 2014-03-25 14:11:34 -0700 | [diff] [blame] | 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 | if true { // disable until bug 7564 is fixed. |
| 175 | return |
| 176 | } |
| 177 | x, y := adjChunks() |
| 178 | |
| 179 | // the pointer inside xs points to y. |
| 180 | xs := x[objsize:] // change objsize to objsize-1 and the test passes |
| 181 | |
| 182 | fin := make(chan bool, 1) |
| 183 | runtime.SetFinalizer(y, func(z *objtype) { fin <- true }) |
| 184 | runtime.GC() |
| 185 | select { |
| 186 | case <-fin: |
| 187 | case <-time.After(4 * time.Second): |
| 188 | t.Errorf("finalizer of next object in memory didn't run") |
| 189 | } |
| 190 | xsglobal = xs // keep empty slice alive until here |
| 191 | } |
| 192 | |
| 193 | var xsglobal []byte |
| 194 | |
| 195 | func adjStringChunk() (string, *objtype) { |
| 196 | b := make([]byte, objsize) |
| 197 | for { |
| 198 | s := string(b) |
| 199 | t := new(objtype) |
| 200 | p := *(*uintptr)(unsafe.Pointer(&s)) |
| 201 | q := uintptr(unsafe.Pointer(t)) |
| 202 | if p+objsize == q { |
| 203 | return s, t |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | // Make sure an empty string on the stack doesn't pin the next object in memory. |
| 209 | func TestEmptyString(t *testing.T) { |
| 210 | x, y := adjStringChunk() |
| 211 | |
| 212 | ss := x[objsize:] // change objsize to objsize-1 and the test passes |
| 213 | fin := make(chan bool, 1) |
| 214 | // set finalizer on string contents of y |
| 215 | runtime.SetFinalizer(y, func(z *objtype) { fin <- true }) |
| 216 | runtime.GC() |
| 217 | select { |
| 218 | case <-fin: |
| 219 | case <-time.After(4 * time.Second): |
| 220 | t.Errorf("finalizer of next string in memory didn't run") |
| 221 | } |
| 222 | ssglobal = ss // keep 0-length string live until here |
| 223 | } |
| 224 | |
| 225 | var ssglobal string |
Dmitriy Vyukov | f4ef697 | 2014-04-02 10:19:28 +0400 | [diff] [blame] | 226 | |
| 227 | // Test for issue 7656. |
| 228 | func TestFinalizerOnGlobal(t *testing.T) { |
| 229 | runtime.SetFinalizer(Foo1, func(p *Object1) {}) |
| 230 | runtime.SetFinalizer(Foo2, func(p *Object2) {}) |
| 231 | runtime.SetFinalizer(Foo1, nil) |
| 232 | runtime.SetFinalizer(Foo2, nil) |
| 233 | } |
| 234 | |
| 235 | type Object1 struct { |
| 236 | Something []byte |
| 237 | } |
| 238 | |
| 239 | type Object2 struct { |
| 240 | Something byte |
| 241 | } |
| 242 | |
| 243 | var ( |
| 244 | Foo2 = &Object2{} |
| 245 | Foo1 = &Object1{} |
| 246 | ) |