Home | History | Annotate | Download | only in runtime
      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