...

Source file src/runtime/mfinal_test.go

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

View as plain text