blob: d2cead2876af9112eecf2494c3f47b37e5a5883f [file] [log] [blame]
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +03001// 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"
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +03009 "testing"
Pieter Droogendijk6350e452013-07-29 19:43:08 +040010 "time"
Keith Randall1b45cc42014-03-25 14:11:34 -070011 "unsafe"
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +030012)
13
Russ Cox5822e782013-08-14 14:54:31 -040014type Tintptr *int // assignable to *int
15type Tint int // *Tint implements Tinter, interface{}
16
17func (t *Tint) m() {}
18
19type Tinter interface {
20 m()
Pieter Droogendijk6350e452013-07-29 19:43:08 +040021}
22
Russ Cox5822e782013-08-14 14:54:31 -040023func TestFinalizerType(t *testing.T) {
Pieter Droogendijk6350e452013-07-29 19:43:08 +040024 if runtime.GOARCH != "amd64" {
25 t.Skipf("Skipping on non-amd64 machine")
26 }
Russ Cox5822e782013-08-14 14:54:31 -040027
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 Cox9a5b0552014-10-06 14:18:09 -040047 for i, tt := range finalizerTests {
Rémy Oudomphenge6b02342013-12-19 21:37:44 +010048 done := make(chan bool, 1)
Russ Cox5f853d72013-10-02 12:30:49 -040049 go func() {
Russ Cox9a5b0552014-10-06 14:18:09 -040050 // 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 Cox5822e782013-08-14 14:54:31 -040058 *v = 97531
59 runtime.SetFinalizer(tt.convert(v), tt.finalizer)
60 v = nil
Rémy Oudomphenge6b02342013-12-19 21:37:44 +010061 done <- true
Russ Cox5822e782013-08-14 14:54:31 -040062 }()
Rémy Oudomphenge6b02342013-12-19 21:37:44 +010063 <-done
Russ Cox5822e782013-08-14 14:54:31 -040064 runtime.GC()
65 select {
66 case <-ch:
67 case <-time.After(time.Second * 4):
Russ Cox9a5b0552014-10-06 14:18:09 -040068 t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer)
Russ Cox5822e782013-08-14 14:54:31 -040069 }
Pieter Droogendijk6350e452013-07-29 19:43:08 +040070 }
71}
72
73type bigValue struct {
74 fill uint64
75 it bool
76 up string
77}
78
79func 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 Oudomphenge6b02342013-12-19 21:37:44 +010084 done := make(chan bool, 1)
Russ Cox5f853d72013-10-02 12:30:49 -040085 go func() {
Pieter Droogendijk6350e452013-07-29 19:43:08 +040086 v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
Russ Cox5f853d72013-10-02 12:30:49 -040087 old := *v
Pieter Droogendijk6350e452013-07-29 19:43:08 +040088 runtime.SetFinalizer(v, func(v interface{}) {
89 i, ok := v.(*bigValue)
90 if !ok {
Russ Cox5f853d72013-10-02 12:30:49 -040091 t.Errorf("finalizer called with type %T, want *bigValue", v)
Pieter Droogendijk6350e452013-07-29 19:43:08 +040092 }
Russ Cox5f853d72013-10-02 12:30:49 -040093 if *i != old {
94 t.Errorf("finalizer called with %+v, want %+v", *i, old)
Pieter Droogendijk6350e452013-07-29 19:43:08 +040095 }
96 close(ch)
97 })
98 v = nil
Rémy Oudomphenge6b02342013-12-19 21:37:44 +010099 done <- true
Pieter Droogendijk6350e452013-07-29 19:43:08 +0400100 }()
Rémy Oudomphenge6b02342013-12-19 21:37:44 +0100101 <-done
Pieter Droogendijk6350e452013-07-29 19:43:08 +0400102 runtime.GC()
103 select {
104 case <-ch:
Russ Cox5f853d72013-10-02 12:30:49 -0400105 case <-time.After(4 * time.Second):
106 t.Errorf("finalizer for type *bigValue didn't run")
Pieter Droogendijk6350e452013-07-29 19:43:08 +0400107 }
108}
109
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +0300110func fin(v *int) {
111}
112
Brad Fitzpatrick4b76a312013-12-17 14:18:58 -0800113// Verify we don't crash at least. golang.org/issue/6857
114func TestFinalizerZeroSizedStruct(t *testing.T) {
115 type Z struct{}
116 z := new(Z)
117 runtime.SetFinalizer(z, func(*Z) {})
118}
119
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +0300120func BenchmarkFinalizer(b *testing.B) {
Dmitriy Vyukov69257d12014-02-24 20:50:12 +0400121 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 Vyukovc14b2682011-10-06 18:42:51 +0300130 }
Dmitriy Vyukov69257d12014-02-24 20:50:12 +0400131 for i := 0; i < Batch; i++ {
132 runtime.SetFinalizer(data[i], nil)
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +0300133 }
Dmitriy Vyukov69257d12014-02-24 20:50:12 +0400134 }
135 })
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +0300136}
137
138func BenchmarkFinalizerRun(b *testing.B) {
Dmitriy Vyukov69257d12014-02-24 20:50:12 +0400139 b.RunParallel(func(pb *testing.PB) {
140 for pb.Next() {
141 v := new(int)
142 runtime.SetFinalizer(v, fin)
143 }
144 })
Dmitriy Vyukovc14b2682011-10-06 18:42:51 +0300145}
Keith Randall1b45cc42014-03-25 14:11:34 -0700146
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
151const objsize = 320
152
153type objtype [objsize]byte
154
155func 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.
173func 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
193var xsglobal []byte
194
195func 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.
209func 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
225var ssglobal string
Dmitriy Vyukovf4ef6972014-04-02 10:19:28 +0400226
227// Test for issue 7656.
228func 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
235type Object1 struct {
236 Something []byte
237}
238
239type Object2 struct {
240 Something byte
241}
242
243var (
244 Foo2 = &Object2{}
245 Foo1 = &Object1{}
246)