| // 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) |
| } |
| } |