Source file src/runtime/list_manual_test.go

     1  // Copyright 2025 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/runtime/sys"
     9  	"runtime"
    10  	"testing"
    11  	"unsafe"
    12  )
    13  
    14  // The tests in this file are identical to list_test.go, but for the
    15  // manually-managed variants.
    16  
    17  type listedValManual struct {
    18  	val int
    19  
    20  	aNode runtime.ListNodeManual
    21  	bNode runtime.ListNodeManual
    22  }
    23  
    24  // ListHeadManual is intended to be used with objects where the lifetime of the
    25  // object is managed explicitly, so it is OK to store as uintptr.
    26  //
    27  // This means that our test values must outlive the test, and must not live on
    28  // the stack (which may move).
    29  var allListedValManual []*listedValManual
    30  
    31  func newListedValManual(v int) *listedValManual {
    32  	lv := &listedValManual{
    33  		val: v,
    34  	}
    35  	allListedValManual = append(allListedValManual, lv)
    36  	return lv
    37  }
    38  
    39  func TestListManualPush(t *testing.T) {
    40  	var headA runtime.ListHeadManual
    41  	headA.Init(unsafe.Offsetof(listedValManual{}.aNode))
    42  
    43  	one := newListedValManual(1)
    44  	headA.Push(unsafe.Pointer(one))
    45  
    46  	two := newListedValManual(2)
    47  	headA.Push(unsafe.Pointer(two))
    48  
    49  	three := newListedValManual(3)
    50  	headA.Push(unsafe.Pointer(three))
    51  
    52  	p := headA.Pop()
    53  	v := (*listedValManual)(p)
    54  	if v == nil {
    55  		t.Fatalf("pop got nil want 3")
    56  	}
    57  	if v.val != 3 {
    58  		t.Errorf("pop got %d want 3", v.val)
    59  	}
    60  
    61  	p = headA.Pop()
    62  	v = (*listedValManual)(p)
    63  	if v == nil {
    64  		t.Fatalf("pop got nil want 2")
    65  	}
    66  	if v.val != 2 {
    67  		t.Errorf("pop got %d want 2", v.val)
    68  	}
    69  
    70  	p = headA.Pop()
    71  	v = (*listedValManual)(p)
    72  	if v == nil {
    73  		t.Fatalf("pop got nil want 1")
    74  	}
    75  	if v.val != 1 {
    76  		t.Errorf("pop got %d want 1", v.val)
    77  	}
    78  
    79  	p = headA.Pop()
    80  	v = (*listedValManual)(p)
    81  	if v != nil {
    82  		t.Fatalf("pop got %+v want nil", v)
    83  	}
    84  
    85  	runtime.KeepAlive(one)
    86  	runtime.KeepAlive(two)
    87  	runtime.KeepAlive(three)
    88  }
    89  
    90  func wantValManual(t *testing.T, v *listedValManual, i int) {
    91  	t.Helper()
    92  	if v == nil {
    93  		t.Fatalf("listedVal got nil want %d", i)
    94  	}
    95  	if v.val != i {
    96  		t.Errorf("pop got %d want %d", v.val, i)
    97  	}
    98  }
    99  
   100  func TestListManualRemoveHead(t *testing.T) {
   101  	var headA runtime.ListHeadManual
   102  	headA.Init(unsafe.Offsetof(listedValManual{}.aNode))
   103  
   104  	one := newListedValManual(1)
   105  	headA.Push(unsafe.Pointer(one))
   106  
   107  	two := newListedValManual(2)
   108  	headA.Push(unsafe.Pointer(two))
   109  
   110  	three := newListedValManual(3)
   111  	headA.Push(unsafe.Pointer(three))
   112  
   113  	headA.Remove(unsafe.Pointer(three))
   114  
   115  	p := headA.Pop()
   116  	v := (*listedValManual)(p)
   117  	wantValManual(t, v, 2)
   118  
   119  	p = headA.Pop()
   120  	v = (*listedValManual)(p)
   121  	wantValManual(t, v, 1)
   122  
   123  	p = headA.Pop()
   124  	v = (*listedValManual)(p)
   125  	if v != nil {
   126  		t.Fatalf("pop got %+v want nil", v)
   127  	}
   128  
   129  	runtime.KeepAlive(one)
   130  	runtime.KeepAlive(two)
   131  	runtime.KeepAlive(three)
   132  }
   133  
   134  func TestListManualRemoveMiddle(t *testing.T) {
   135  	var headA runtime.ListHeadManual
   136  	headA.Init(unsafe.Offsetof(listedValManual{}.aNode))
   137  
   138  	one := newListedValManual(1)
   139  	headA.Push(unsafe.Pointer(one))
   140  
   141  	two := newListedValManual(2)
   142  	headA.Push(unsafe.Pointer(two))
   143  
   144  	three := newListedValManual(3)
   145  	headA.Push(unsafe.Pointer(three))
   146  
   147  	headA.Remove(unsafe.Pointer(two))
   148  
   149  	p := headA.Pop()
   150  	v := (*listedValManual)(p)
   151  	wantValManual(t, v, 3)
   152  
   153  	p = headA.Pop()
   154  	v = (*listedValManual)(p)
   155  	wantValManual(t, v, 1)
   156  
   157  	p = headA.Pop()
   158  	v = (*listedValManual)(p)
   159  	if v != nil {
   160  		t.Fatalf("pop got %+v want nil", v)
   161  	}
   162  
   163  	runtime.KeepAlive(one)
   164  	runtime.KeepAlive(two)
   165  	runtime.KeepAlive(three)
   166  }
   167  
   168  func TestListManualRemoveTail(t *testing.T) {
   169  	var headA runtime.ListHeadManual
   170  	headA.Init(unsafe.Offsetof(listedValManual{}.aNode))
   171  
   172  	one := newListedValManual(1)
   173  	headA.Push(unsafe.Pointer(one))
   174  
   175  	two := newListedValManual(2)
   176  	headA.Push(unsafe.Pointer(two))
   177  
   178  	three := newListedValManual(3)
   179  	headA.Push(unsafe.Pointer(three))
   180  
   181  	headA.Remove(unsafe.Pointer(one))
   182  
   183  	p := headA.Pop()
   184  	v := (*listedValManual)(p)
   185  	wantValManual(t, v, 3)
   186  
   187  	p = headA.Pop()
   188  	v = (*listedValManual)(p)
   189  	wantValManual(t, v, 2)
   190  
   191  	p = headA.Pop()
   192  	v = (*listedValManual)(p)
   193  	if v != nil {
   194  		t.Fatalf("pop got %+v want nil", v)
   195  	}
   196  
   197  	runtime.KeepAlive(one)
   198  	runtime.KeepAlive(two)
   199  	runtime.KeepAlive(three)
   200  }
   201  
   202  func TestListManualRemoveAll(t *testing.T) {
   203  	var headA runtime.ListHeadManual
   204  	headA.Init(unsafe.Offsetof(listedValManual{}.aNode))
   205  
   206  	one := newListedValManual(1)
   207  	headA.Push(unsafe.Pointer(one))
   208  
   209  	two := newListedValManual(2)
   210  	headA.Push(unsafe.Pointer(two))
   211  
   212  	three := newListedValManual(3)
   213  	headA.Push(unsafe.Pointer(three))
   214  
   215  	headA.Remove(unsafe.Pointer(one))
   216  	headA.Remove(unsafe.Pointer(two))
   217  	headA.Remove(unsafe.Pointer(three))
   218  
   219  	p := headA.Pop()
   220  	v := (*listedValManual)(p)
   221  	if v != nil {
   222  		t.Fatalf("pop got %+v want nil", v)
   223  	}
   224  
   225  	runtime.KeepAlive(one)
   226  	runtime.KeepAlive(two)
   227  	runtime.KeepAlive(three)
   228  }
   229  
   230  // The tests below are identical, but used with a sys.NotInHeap type.
   231  
   232  type listedValNIH struct {
   233  	_ sys.NotInHeap
   234  	listedValManual
   235  }
   236  
   237  func newListedValNIH(v int) *listedValNIH {
   238  	l := (*listedValNIH)(runtime.PersistentAlloc(unsafe.Sizeof(listedValNIH{}), unsafe.Alignof(listedValNIH{})))
   239  	l.val = v
   240  	return l
   241  }
   242  
   243  func newListHeadNIH() *runtime.ListHeadManual {
   244  	return (*runtime.ListHeadManual)(runtime.PersistentAlloc(unsafe.Sizeof(runtime.ListHeadManual{}), unsafe.Alignof(runtime.ListHeadManual{})))
   245  }
   246  
   247  func TestListNIHPush(t *testing.T) {
   248  	headA := newListHeadNIH()
   249  	headA.Init(unsafe.Offsetof(listedValNIH{}.aNode))
   250  
   251  	one := newListedValNIH(1)
   252  	headA.Push(unsafe.Pointer(one))
   253  
   254  	two := newListedValNIH(2)
   255  	headA.Push(unsafe.Pointer(two))
   256  
   257  	three := newListedValNIH(3)
   258  	headA.Push(unsafe.Pointer(three))
   259  
   260  	p := headA.Pop()
   261  	v := (*listedValNIH)(p)
   262  	if v == nil {
   263  		t.Fatalf("pop got nil want 3")
   264  	}
   265  	if v.val != 3 {
   266  		t.Errorf("pop got %d want 3", v.val)
   267  	}
   268  
   269  	p = headA.Pop()
   270  	v = (*listedValNIH)(p)
   271  	if v == nil {
   272  		t.Fatalf("pop got nil want 2")
   273  	}
   274  	if v.val != 2 {
   275  		t.Errorf("pop got %d want 2", v.val)
   276  	}
   277  
   278  	p = headA.Pop()
   279  	v = (*listedValNIH)(p)
   280  	if v == nil {
   281  		t.Fatalf("pop got nil want 1")
   282  	}
   283  	if v.val != 1 {
   284  		t.Errorf("pop got %d want 1", v.val)
   285  	}
   286  
   287  	p = headA.Pop()
   288  	v = (*listedValNIH)(p)
   289  	if v != nil {
   290  		t.Fatalf("pop got %+v want nil", v)
   291  	}
   292  }
   293  
   294  func wantValNIH(t *testing.T, v *listedValNIH, i int) {
   295  	t.Helper()
   296  	if v == nil {
   297  		t.Fatalf("listedVal got nil want %d", i)
   298  	}
   299  	if v.val != i {
   300  		t.Errorf("pop got %d want %d", v.val, i)
   301  	}
   302  }
   303  
   304  func TestListNIHRemoveHead(t *testing.T) {
   305  	headA := newListHeadNIH()
   306  	headA.Init(unsafe.Offsetof(listedValNIH{}.aNode))
   307  
   308  	one := newListedValNIH(1)
   309  	headA.Push(unsafe.Pointer(one))
   310  
   311  	two := newListedValNIH(2)
   312  	headA.Push(unsafe.Pointer(two))
   313  
   314  	three := newListedValNIH(3)
   315  	headA.Push(unsafe.Pointer(three))
   316  
   317  	headA.Remove(unsafe.Pointer(three))
   318  
   319  	p := headA.Pop()
   320  	v := (*listedValNIH)(p)
   321  	wantValNIH(t, v, 2)
   322  
   323  	p = headA.Pop()
   324  	v = (*listedValNIH)(p)
   325  	wantValNIH(t, v, 1)
   326  
   327  	p = headA.Pop()
   328  	v = (*listedValNIH)(p)
   329  	if v != nil {
   330  		t.Fatalf("pop got %+v want nil", v)
   331  	}
   332  }
   333  
   334  func TestListNIHRemoveMiddle(t *testing.T) {
   335  	headA := newListHeadNIH()
   336  	headA.Init(unsafe.Offsetof(listedValNIH{}.aNode))
   337  
   338  	one := newListedValNIH(1)
   339  	headA.Push(unsafe.Pointer(one))
   340  
   341  	two := newListedValNIH(2)
   342  	headA.Push(unsafe.Pointer(two))
   343  
   344  	three := newListedValNIH(3)
   345  	headA.Push(unsafe.Pointer(three))
   346  
   347  	headA.Remove(unsafe.Pointer(two))
   348  
   349  	p := headA.Pop()
   350  	v := (*listedValNIH)(p)
   351  	wantValNIH(t, v, 3)
   352  
   353  	p = headA.Pop()
   354  	v = (*listedValNIH)(p)
   355  	wantValNIH(t, v, 1)
   356  
   357  	p = headA.Pop()
   358  	v = (*listedValNIH)(p)
   359  	if v != nil {
   360  		t.Fatalf("pop got %+v want nil", v)
   361  	}
   362  }
   363  
   364  func TestListNIHRemoveTail(t *testing.T) {
   365  	headA := newListHeadNIH()
   366  	headA.Init(unsafe.Offsetof(listedValNIH{}.aNode))
   367  
   368  	one := newListedValNIH(1)
   369  	headA.Push(unsafe.Pointer(one))
   370  
   371  	two := newListedValNIH(2)
   372  	headA.Push(unsafe.Pointer(two))
   373  
   374  	three := newListedValNIH(3)
   375  	headA.Push(unsafe.Pointer(three))
   376  
   377  	headA.Remove(unsafe.Pointer(one))
   378  
   379  	p := headA.Pop()
   380  	v := (*listedValNIH)(p)
   381  	wantValNIH(t, v, 3)
   382  
   383  	p = headA.Pop()
   384  	v = (*listedValNIH)(p)
   385  	wantValNIH(t, v, 2)
   386  
   387  	p = headA.Pop()
   388  	v = (*listedValNIH)(p)
   389  	if v != nil {
   390  		t.Fatalf("pop got %+v want nil", v)
   391  	}
   392  }
   393  
   394  func TestListNIHRemoveAll(t *testing.T) {
   395  	headA := newListHeadNIH()
   396  	headA.Init(unsafe.Offsetof(listedValNIH{}.aNode))
   397  
   398  	one := newListedValNIH(1)
   399  	headA.Push(unsafe.Pointer(one))
   400  
   401  	two := newListedValNIH(2)
   402  	headA.Push(unsafe.Pointer(two))
   403  
   404  	three := newListedValNIH(3)
   405  	headA.Push(unsafe.Pointer(three))
   406  
   407  	headA.Remove(unsafe.Pointer(one))
   408  	headA.Remove(unsafe.Pointer(two))
   409  	headA.Remove(unsafe.Pointer(three))
   410  
   411  	p := headA.Pop()
   412  	v := (*listedValNIH)(p)
   413  	if v != nil {
   414  		t.Fatalf("pop got %+v want nil", v)
   415  	}
   416  }
   417  

View as plain text