...
Run Format

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  	}
    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  
   245  func TestDeferKeepAlive(t *testing.T) {
   246  	if *flagQuick {
   247  		t.Skip("-quick")
   248  	}
   249  
   250  	// See issue 21402.
   251  	t.Parallel()
   252  	type T *int // needs to be a pointer base type to avoid tinyalloc and its never-finalized behavior.
   253  	x := new(T)
   254  	finRun := false
   255  	runtime.SetFinalizer(x, func(x *T) {
   256  		finRun = true
   257  	})
   258  	defer runtime.KeepAlive(x)
   259  	runtime.GC()
   260  	time.Sleep(time.Second)
   261  	if finRun {
   262  		t.Errorf("finalizer ran prematurely")
   263  	}
   264  }
   265  

View as plain text