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

View as plain text