123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- // Copyright 2011 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package runtime_test
- import (
- "runtime"
- "testing"
- "time"
- "unsafe"
- )
- type Tintptr *int // assignable to *int
- type Tint int // *Tint implements Tinter, interface{}
- func (t *Tint) m() {}
- type Tinter interface {
- m()
- }
- func TestFinalizerType(t *testing.T) {
- if runtime.GOARCH != "amd64" {
- t.Skipf("Skipping on non-amd64 machine")
- }
- if runtime.Compiler == "gccgo" {
- t.Skip("skipping for gccgo")
- }
- ch := make(chan bool, 10)
- finalize := func(x *int) {
- if *x != 97531 {
- t.Errorf("finalizer %d, want %d", *x, 97531)
- }
- ch <- true
- }
- var finalizerTests = []struct {
- convert func(*int) interface{}
- finalizer interface{}
- }{
- {func(x *int) interface{} { return x }, func(v *int) { finalize(v) }},
- {func(x *int) interface{} { return Tintptr(x) }, func(v Tintptr) { finalize(v) }},
- {func(x *int) interface{} { return Tintptr(x) }, func(v *int) { finalize(v) }},
- {func(x *int) interface{} { return (*Tint)(x) }, func(v *Tint) { finalize((*int)(v)) }},
- {func(x *int) interface{} { return (*Tint)(x) }, func(v Tinter) { finalize((*int)(v.(*Tint))) }},
- }
- for i, tt := range finalizerTests {
- done := make(chan bool, 1)
- go func() {
- // allocate struct with pointer to avoid hitting tinyalloc.
- // Otherwise we can't be sure when the allocation will
- // be freed.
- type T struct {
- v int
- p unsafe.Pointer
- }
- v := &new(T).v
- *v = 97531
- runtime.SetFinalizer(tt.convert(v), tt.finalizer)
- v = nil
- done <- true
- }()
- <-done
- runtime.GC()
- select {
- case <-ch:
- case <-time.After(time.Second * 4):
- t.Errorf("#%d: finalizer for type %T didn't run", i, tt.finalizer)
- }
- }
- }
- type bigValue struct {
- fill uint64
- it bool
- up string
- }
- func TestFinalizerInterfaceBig(t *testing.T) {
- if runtime.GOARCH != "amd64" {
- t.Skipf("Skipping on non-amd64 machine")
- }
- if runtime.Compiler == "gccgo" {
- t.Skip("skipping for gccgo")
- }
- ch := make(chan bool)
- done := make(chan bool, 1)
- go func() {
- v := &bigValue{0xDEADBEEFDEADBEEF, true, "It matters not how strait the gate"}
- old := *v
- runtime.SetFinalizer(v, func(v interface{}) {
- i, ok := v.(*bigValue)
- if !ok {
- t.Errorf("finalizer called with type %T, want *bigValue", v)
- }
- if *i != old {
- t.Errorf("finalizer called with %+v, want %+v", *i, old)
- }
- close(ch)
- })
- v = nil
- done <- true
- }()
- <-done
- runtime.GC()
- select {
- case <-ch:
- case <-time.After(4 * time.Second):
- t.Errorf("finalizer for type *bigValue didn't run")
- }
- }
- func fin(v *int) {
- }
- // Verify we don't crash at least. golang.org/issue/6857
- func TestFinalizerZeroSizedStruct(t *testing.T) {
- type Z struct{}
- z := new(Z)
- runtime.SetFinalizer(z, func(*Z) {})
- }
- func BenchmarkFinalizer(b *testing.B) {
- const Batch = 1000
- b.RunParallel(func(pb *testing.PB) {
- var data [Batch]*int
- for i := 0; i < Batch; i++ {
- data[i] = new(int)
- }
- for pb.Next() {
- for i := 0; i < Batch; i++ {
- runtime.SetFinalizer(data[i], fin)
- }
- for i := 0; i < Batch; i++ {
- runtime.SetFinalizer(data[i], nil)
- }
- }
- })
- }
- func BenchmarkFinalizerRun(b *testing.B) {
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- v := new(int)
- runtime.SetFinalizer(v, fin)
- }
- })
- }
- // One chunk must be exactly one sizeclass in size.
- // It should be a sizeclass not used much by others, so we
- // have a greater chance of finding adjacent ones.
- // size class 19: 320 byte objects, 25 per page, 1 page alloc at a time
- const objsize = 320
- type objtype [objsize]byte
- func adjChunks() (*objtype, *objtype) {
- var s []*objtype
- for {
- c := new(objtype)
- for _, d := range s {
- if uintptr(unsafe.Pointer(c))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(d)) {
- return c, d
- }
- if uintptr(unsafe.Pointer(d))+unsafe.Sizeof(*c) == uintptr(unsafe.Pointer(c)) {
- return d, c
- }
- }
- s = append(s, c)
- }
- }
- // Make sure an empty slice on the stack doesn't pin the next object in memory.
- func TestEmptySlice(t *testing.T) {
- if true { // disable until bug 7564 is fixed.
- return
- }
- if runtime.Compiler == "gccgo" {
- t.Skip("skipping for gccgo")
- }
- x, y := adjChunks()
- // the pointer inside xs points to y.
- xs := x[objsize:] // change objsize to objsize-1 and the test passes
- fin := make(chan bool, 1)
- runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
- runtime.GC()
- select {
- case <-fin:
- case <-time.After(4 * time.Second):
- t.Errorf("finalizer of next object in memory didn't run")
- }
- xsglobal = xs // keep empty slice alive until here
- }
- var xsglobal []byte
- func adjStringChunk() (string, *objtype) {
- b := make([]byte, objsize)
- for {
- s := string(b)
- t := new(objtype)
- p := *(*uintptr)(unsafe.Pointer(&s))
- q := uintptr(unsafe.Pointer(t))
- if p+objsize == q {
- return s, t
- }
- }
- }
- // Make sure an empty string on the stack doesn't pin the next object in memory.
- func TestEmptyString(t *testing.T) {
- if runtime.Compiler == "gccgo" {
- t.Skip("skipping for gccgo")
- }
- x, y := adjStringChunk()
- ss := x[objsize:] // change objsize to objsize-1 and the test passes
- fin := make(chan bool, 1)
- // set finalizer on string contents of y
- runtime.SetFinalizer(y, func(z *objtype) { fin <- true })
- runtime.GC()
- select {
- case <-fin:
- case <-time.After(4 * time.Second):
- t.Errorf("finalizer of next string in memory didn't run")
- }
- ssglobal = ss // keep 0-length string live until here
- }
- var ssglobal string
- // Test for issue 7656.
- func TestFinalizerOnGlobal(t *testing.T) {
- runtime.SetFinalizer(Foo1, func(p *Object1) {})
- runtime.SetFinalizer(Foo2, func(p *Object2) {})
- runtime.SetFinalizer(Foo1, nil)
- runtime.SetFinalizer(Foo2, nil)
- }
- type Object1 struct {
- Something []byte
- }
- type Object2 struct {
- Something byte
- }
- var (
- Foo2 = &Object2{}
- Foo1 = &Object1{}
- )
|