// Copyright 2025 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package runtime_test import ( "internal/runtime/sys" "runtime" "testing" "unsafe" ) // The tests in this file are identical to list_test.go, but for the // manually-managed variants. type listedValManual struct { val int aNode runtime.ListNodeManual bNode runtime.ListNodeManual } // ListHeadManual is intended to be used with objects where the lifetime of the // object is managed explicitly, so it is OK to store as uintptr. // // This means that our test values must outlive the test, and must not live on // the stack (which may move). var allListedValManual []*listedValManual func newListedValManual(v int) *listedValManual { lv := &listedValManual{ val: v, } allListedValManual = append(allListedValManual, lv) return lv } func TestListManualPush(t *testing.T) { var headA runtime.ListHeadManual headA.Init(unsafe.Offsetof(listedValManual{}.aNode)) one := newListedValManual(1) headA.Push(unsafe.Pointer(one)) two := newListedValManual(2) headA.Push(unsafe.Pointer(two)) three := newListedValManual(3) headA.Push(unsafe.Pointer(three)) p := headA.Pop() v := (*listedValManual)(p) if v == nil { t.Fatalf("pop got nil want 3") } if v.val != 3 { t.Errorf("pop got %d want 3", v.val) } p = headA.Pop() v = (*listedValManual)(p) if v == nil { t.Fatalf("pop got nil want 2") } if v.val != 2 { t.Errorf("pop got %d want 2", v.val) } p = headA.Pop() v = (*listedValManual)(p) if v == nil { t.Fatalf("pop got nil want 1") } if v.val != 1 { t.Errorf("pop got %d want 1", v.val) } p = headA.Pop() v = (*listedValManual)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } runtime.KeepAlive(one) runtime.KeepAlive(two) runtime.KeepAlive(three) } func wantValManual(t *testing.T, v *listedValManual, i int) { t.Helper() if v == nil { t.Fatalf("listedVal got nil want %d", i) } if v.val != i { t.Errorf("pop got %d want %d", v.val, i) } } func TestListManualRemoveHead(t *testing.T) { var headA runtime.ListHeadManual headA.Init(unsafe.Offsetof(listedValManual{}.aNode)) one := newListedValManual(1) headA.Push(unsafe.Pointer(one)) two := newListedValManual(2) headA.Push(unsafe.Pointer(two)) three := newListedValManual(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(three)) p := headA.Pop() v := (*listedValManual)(p) wantValManual(t, v, 2) p = headA.Pop() v = (*listedValManual)(p) wantValManual(t, v, 1) p = headA.Pop() v = (*listedValManual)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } runtime.KeepAlive(one) runtime.KeepAlive(two) runtime.KeepAlive(three) } func TestListManualRemoveMiddle(t *testing.T) { var headA runtime.ListHeadManual headA.Init(unsafe.Offsetof(listedValManual{}.aNode)) one := newListedValManual(1) headA.Push(unsafe.Pointer(one)) two := newListedValManual(2) headA.Push(unsafe.Pointer(two)) three := newListedValManual(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(two)) p := headA.Pop() v := (*listedValManual)(p) wantValManual(t, v, 3) p = headA.Pop() v = (*listedValManual)(p) wantValManual(t, v, 1) p = headA.Pop() v = (*listedValManual)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } runtime.KeepAlive(one) runtime.KeepAlive(two) runtime.KeepAlive(three) } func TestListManualRemoveTail(t *testing.T) { var headA runtime.ListHeadManual headA.Init(unsafe.Offsetof(listedValManual{}.aNode)) one := newListedValManual(1) headA.Push(unsafe.Pointer(one)) two := newListedValManual(2) headA.Push(unsafe.Pointer(two)) three := newListedValManual(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(one)) p := headA.Pop() v := (*listedValManual)(p) wantValManual(t, v, 3) p = headA.Pop() v = (*listedValManual)(p) wantValManual(t, v, 2) p = headA.Pop() v = (*listedValManual)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } runtime.KeepAlive(one) runtime.KeepAlive(two) runtime.KeepAlive(three) } func TestListManualRemoveAll(t *testing.T) { var headA runtime.ListHeadManual headA.Init(unsafe.Offsetof(listedValManual{}.aNode)) one := newListedValManual(1) headA.Push(unsafe.Pointer(one)) two := newListedValManual(2) headA.Push(unsafe.Pointer(two)) three := newListedValManual(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(one)) headA.Remove(unsafe.Pointer(two)) headA.Remove(unsafe.Pointer(three)) p := headA.Pop() v := (*listedValManual)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } runtime.KeepAlive(one) runtime.KeepAlive(two) runtime.KeepAlive(three) } // The tests below are identical, but used with a sys.NotInHeap type. type listedValNIH struct { _ sys.NotInHeap listedValManual } func newListedValNIH(v int) *listedValNIH { l := (*listedValNIH)(runtime.PersistentAlloc(unsafe.Sizeof(listedValNIH{}), unsafe.Alignof(listedValNIH{}))) l.val = v return l } func newListHeadNIH() *runtime.ListHeadManual { return (*runtime.ListHeadManual)(runtime.PersistentAlloc(unsafe.Sizeof(runtime.ListHeadManual{}), unsafe.Alignof(runtime.ListHeadManual{}))) } func TestListNIHPush(t *testing.T) { headA := newListHeadNIH() headA.Init(unsafe.Offsetof(listedValNIH{}.aNode)) one := newListedValNIH(1) headA.Push(unsafe.Pointer(one)) two := newListedValNIH(2) headA.Push(unsafe.Pointer(two)) three := newListedValNIH(3) headA.Push(unsafe.Pointer(three)) p := headA.Pop() v := (*listedValNIH)(p) if v == nil { t.Fatalf("pop got nil want 3") } if v.val != 3 { t.Errorf("pop got %d want 3", v.val) } p = headA.Pop() v = (*listedValNIH)(p) if v == nil { t.Fatalf("pop got nil want 2") } if v.val != 2 { t.Errorf("pop got %d want 2", v.val) } p = headA.Pop() v = (*listedValNIH)(p) if v == nil { t.Fatalf("pop got nil want 1") } if v.val != 1 { t.Errorf("pop got %d want 1", v.val) } p = headA.Pop() v = (*listedValNIH)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } } func wantValNIH(t *testing.T, v *listedValNIH, i int) { t.Helper() if v == nil { t.Fatalf("listedVal got nil want %d", i) } if v.val != i { t.Errorf("pop got %d want %d", v.val, i) } } func TestListNIHRemoveHead(t *testing.T) { headA := newListHeadNIH() headA.Init(unsafe.Offsetof(listedValNIH{}.aNode)) one := newListedValNIH(1) headA.Push(unsafe.Pointer(one)) two := newListedValNIH(2) headA.Push(unsafe.Pointer(two)) three := newListedValNIH(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(three)) p := headA.Pop() v := (*listedValNIH)(p) wantValNIH(t, v, 2) p = headA.Pop() v = (*listedValNIH)(p) wantValNIH(t, v, 1) p = headA.Pop() v = (*listedValNIH)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } } func TestListNIHRemoveMiddle(t *testing.T) { headA := newListHeadNIH() headA.Init(unsafe.Offsetof(listedValNIH{}.aNode)) one := newListedValNIH(1) headA.Push(unsafe.Pointer(one)) two := newListedValNIH(2) headA.Push(unsafe.Pointer(two)) three := newListedValNIH(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(two)) p := headA.Pop() v := (*listedValNIH)(p) wantValNIH(t, v, 3) p = headA.Pop() v = (*listedValNIH)(p) wantValNIH(t, v, 1) p = headA.Pop() v = (*listedValNIH)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } } func TestListNIHRemoveTail(t *testing.T) { headA := newListHeadNIH() headA.Init(unsafe.Offsetof(listedValNIH{}.aNode)) one := newListedValNIH(1) headA.Push(unsafe.Pointer(one)) two := newListedValNIH(2) headA.Push(unsafe.Pointer(two)) three := newListedValNIH(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(one)) p := headA.Pop() v := (*listedValNIH)(p) wantValNIH(t, v, 3) p = headA.Pop() v = (*listedValNIH)(p) wantValNIH(t, v, 2) p = headA.Pop() v = (*listedValNIH)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } } func TestListNIHRemoveAll(t *testing.T) { headA := newListHeadNIH() headA.Init(unsafe.Offsetof(listedValNIH{}.aNode)) one := newListedValNIH(1) headA.Push(unsafe.Pointer(one)) two := newListedValNIH(2) headA.Push(unsafe.Pointer(two)) three := newListedValNIH(3) headA.Push(unsafe.Pointer(three)) headA.Remove(unsafe.Pointer(one)) headA.Remove(unsafe.Pointer(two)) headA.Remove(unsafe.Pointer(three)) p := headA.Pop() v := (*listedValNIH)(p) if v != nil { t.Fatalf("pop got %+v want nil", v) } }