Source file src/runtime/mfinal_test.go

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

View as plain text