Source file src/runtime/mcleanup_test.go

     1  // Copyright 2024 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  	"unsafe"
    11  )
    12  
    13  func TestCleanup(t *testing.T) {
    14  	ch := make(chan bool, 1)
    15  	done := make(chan bool, 1)
    16  	want := 97531
    17  	go func() {
    18  		// allocate struct with pointer to avoid hitting tinyalloc.
    19  		// Otherwise we can't be sure when the allocation will
    20  		// be freed.
    21  		type T struct {
    22  			v int
    23  			p unsafe.Pointer
    24  		}
    25  		v := &new(T).v
    26  		*v = 97531
    27  		cleanup := func(x int) {
    28  			if x != want {
    29  				t.Errorf("cleanup %d, want %d", x, want)
    30  			}
    31  			ch <- true
    32  		}
    33  		runtime.AddCleanup(v, cleanup, 97531)
    34  		v = nil
    35  		done <- true
    36  	}()
    37  	<-done
    38  	runtime.GC()
    39  	<-ch
    40  }
    41  
    42  func TestCleanupMultiple(t *testing.T) {
    43  	ch := make(chan bool, 3)
    44  	done := make(chan bool, 1)
    45  	want := 97531
    46  	go func() {
    47  		// allocate struct with pointer to avoid hitting tinyalloc.
    48  		// Otherwise we can't be sure when the allocation will
    49  		// be freed.
    50  		type T struct {
    51  			v int
    52  			p unsafe.Pointer
    53  		}
    54  		v := &new(T).v
    55  		*v = 97531
    56  		cleanup := func(x int) {
    57  			if x != want {
    58  				t.Errorf("cleanup %d, want %d", x, want)
    59  			}
    60  			ch <- true
    61  		}
    62  		runtime.AddCleanup(v, cleanup, 97531)
    63  		runtime.AddCleanup(v, cleanup, 97531)
    64  		runtime.AddCleanup(v, cleanup, 97531)
    65  		v = nil
    66  		done <- true
    67  	}()
    68  	<-done
    69  	runtime.GC()
    70  	<-ch
    71  	<-ch
    72  	<-ch
    73  }
    74  
    75  func TestCleanupZeroSizedStruct(t *testing.T) {
    76  	type Z struct{}
    77  	z := new(Z)
    78  	runtime.AddCleanup(z, func(s string) {}, "foo")
    79  }
    80  
    81  func TestCleanupAfterFinalizer(t *testing.T) {
    82  	ch := make(chan int, 2)
    83  	done := make(chan bool, 1)
    84  	want := 97531
    85  	go func() {
    86  		// allocate struct with pointer to avoid hitting tinyalloc.
    87  		// Otherwise we can't be sure when the allocation will
    88  		// be freed.
    89  		type T struct {
    90  			v int
    91  			p unsafe.Pointer
    92  		}
    93  		v := &new(T).v
    94  		*v = 97531
    95  		finalizer := func(x *int) {
    96  			ch <- 1
    97  		}
    98  		cleanup := func(x int) {
    99  			if x != want {
   100  				t.Errorf("cleanup %d, want %d", x, want)
   101  			}
   102  			ch <- 2
   103  		}
   104  		runtime.AddCleanup(v, cleanup, 97531)
   105  		runtime.SetFinalizer(v, finalizer)
   106  		v = nil
   107  		done <- true
   108  	}()
   109  	<-done
   110  	runtime.GC()
   111  	var result int
   112  	result = <-ch
   113  	if result != 1 {
   114  		t.Errorf("result %d, want 1", result)
   115  	}
   116  	runtime.GC()
   117  	result = <-ch
   118  	if result != 2 {
   119  		t.Errorf("result %d, want 2", result)
   120  	}
   121  }
   122  
   123  func TestCleanupInteriorPointer(t *testing.T) {
   124  	ch := make(chan bool, 3)
   125  	done := make(chan bool, 1)
   126  	want := 97531
   127  	go func() {
   128  		// Allocate struct with pointer to avoid hitting tinyalloc.
   129  		// Otherwise we can't be sure when the allocation will
   130  		// be freed.
   131  		type T struct {
   132  			p unsafe.Pointer
   133  			i int
   134  			a int
   135  			b int
   136  			c int
   137  		}
   138  		ts := new(T)
   139  		ts.a = 97531
   140  		ts.b = 97531
   141  		ts.c = 97531
   142  		cleanup := func(x int) {
   143  			if x != want {
   144  				t.Errorf("cleanup %d, want %d", x, want)
   145  			}
   146  			ch <- true
   147  		}
   148  		runtime.AddCleanup(&ts.a, cleanup, 97531)
   149  		runtime.AddCleanup(&ts.b, cleanup, 97531)
   150  		runtime.AddCleanup(&ts.c, cleanup, 97531)
   151  		ts = nil
   152  		done <- true
   153  	}()
   154  	<-done
   155  	runtime.GC()
   156  	<-ch
   157  	<-ch
   158  	<-ch
   159  }
   160  
   161  func TestCleanupStop(t *testing.T) {
   162  	done := make(chan bool, 1)
   163  	go func() {
   164  		// allocate struct with pointer to avoid hitting tinyalloc.
   165  		// Otherwise we can't be sure when the allocation will
   166  		// be freed.
   167  		type T struct {
   168  			v int
   169  			p unsafe.Pointer
   170  		}
   171  		v := &new(T).v
   172  		*v = 97531
   173  		cleanup := func(x int) {
   174  			t.Error("cleanup called, want no cleanup called")
   175  		}
   176  		c := runtime.AddCleanup(v, cleanup, 97531)
   177  		c.Stop()
   178  		v = nil
   179  		done <- true
   180  	}()
   181  	<-done
   182  	runtime.GC()
   183  }
   184  
   185  func TestCleanupStopMultiple(t *testing.T) {
   186  	done := make(chan bool, 1)
   187  	go func() {
   188  		// allocate struct with pointer to avoid hitting tinyalloc.
   189  		// Otherwise we can't be sure when the allocation will
   190  		// be freed.
   191  		type T struct {
   192  			v int
   193  			p unsafe.Pointer
   194  		}
   195  		v := &new(T).v
   196  		*v = 97531
   197  		cleanup := func(x int) {
   198  			t.Error("cleanup called, want no cleanup called")
   199  		}
   200  		c := runtime.AddCleanup(v, cleanup, 97531)
   201  		c.Stop()
   202  		c.Stop()
   203  		c.Stop()
   204  		v = nil
   205  		done <- true
   206  	}()
   207  	<-done
   208  	runtime.GC()
   209  }
   210  
   211  func TestCleanupStopinterleavedMultiple(t *testing.T) {
   212  	ch := make(chan bool, 3)
   213  	done := make(chan bool, 1)
   214  	go func() {
   215  		// allocate struct with pointer to avoid hitting tinyalloc.
   216  		// Otherwise we can't be sure when the allocation will
   217  		// be freed.
   218  		type T struct {
   219  			v int
   220  			p unsafe.Pointer
   221  		}
   222  		v := &new(T).v
   223  		*v = 97531
   224  		cleanup := func(x int) {
   225  			if x != 1 {
   226  				t.Error("cleanup called, want no cleanup called")
   227  			}
   228  			ch <- true
   229  		}
   230  		runtime.AddCleanup(v, cleanup, 1)
   231  		runtime.AddCleanup(v, cleanup, 2).Stop()
   232  		runtime.AddCleanup(v, cleanup, 1)
   233  		runtime.AddCleanup(v, cleanup, 2).Stop()
   234  		runtime.AddCleanup(v, cleanup, 1)
   235  		v = nil
   236  		done <- true
   237  	}()
   238  	<-done
   239  	runtime.GC()
   240  	<-ch
   241  	<-ch
   242  	<-ch
   243  }
   244  
   245  func TestCleanupStopAfterCleanupRuns(t *testing.T) {
   246  	ch := make(chan bool, 1)
   247  	done := make(chan bool, 1)
   248  	var stop func()
   249  	go func() {
   250  		// Allocate struct with pointer to avoid hitting tinyalloc.
   251  		// Otherwise we can't be sure when the allocation will
   252  		// be freed.
   253  		type T struct {
   254  			v int
   255  			p unsafe.Pointer
   256  		}
   257  		v := &new(T).v
   258  		*v = 97531
   259  		cleanup := func(x int) {
   260  			ch <- true
   261  		}
   262  		cl := runtime.AddCleanup(v, cleanup, 97531)
   263  		v = nil
   264  		stop = cl.Stop
   265  		done <- true
   266  	}()
   267  	<-done
   268  	runtime.GC()
   269  	<-ch
   270  	stop()
   271  }
   272  

View as plain text