var DebugDecorateMappings = &debug.decoratemappings
func SetVMANameSupported() bool { return setVMANameSupported() }
+
+type ListHead struct {
+ l listHead
+}
+
+func (head *ListHead) Init(off uintptr) {
+ head.l.init(off)
+}
+
+type ListNode struct {
+ l listNode
+}
+
+func (head *ListHead) Push(p unsafe.Pointer) {
+ head.l.push(p)
+}
+
+func (head *ListHead) Pop() unsafe.Pointer {
+ return head.l.pop()
+}
+
+func (head *ListHead) Remove(p unsafe.Pointer) {
+ head.l.remove(p)
+}
+
+type ListHeadManual struct {
+ l listHeadManual
+}
+
+func (head *ListHeadManual) Init(off uintptr) {
+ head.l.init(off)
+}
+
+type ListNodeManual struct {
+ l listNodeManual
+}
+
+func (head *ListHeadManual) Push(p unsafe.Pointer) {
+ head.l.push(p)
+}
+
+func (head *ListHeadManual) Pop() unsafe.Pointer {
+ return head.l.pop()
+}
+
+func (head *ListHeadManual) Remove(p unsafe.Pointer) {
+ head.l.remove(p)
+}
--- /dev/null
+// 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
+
+import (
+ "unsafe"
+)
+
+// listHead points to the head of an intrusive doubly-linked list.
+//
+// Prior to use, you must call init to store the offset of listNode fields.
+//
+// Every object in the list should be the same type.
+type listHead struct {
+ obj unsafe.Pointer
+
+ initialized bool
+ nodeOffset uintptr
+}
+
+// init initializes the list head. off is the offset (via unsafe.Offsetof) of
+// the listNode field in the objects in the list.
+func (head *listHead) init(off uintptr) {
+ head.initialized = true
+ head.nodeOffset = off
+}
+
+// listNode is the linked list node for objects in a listHead list.
+//
+// listNode must be stored as a field in objects placed in the linked list. The
+// offset of the field is registered via listHead.init.
+//
+// For example:
+//
+// type foo struct {
+// val int
+//
+// node listNode
+// }
+//
+// var fooHead listHead
+// fooHead.init(unsafe.Offsetof(foo{}.node))
+type listNode struct {
+ prev unsafe.Pointer
+ next unsafe.Pointer
+}
+
+func (head *listHead) getNode(p unsafe.Pointer) *listNode {
+ if !head.initialized {
+ throw("runtime: uninitialized listHead")
+ }
+
+ if p == nil {
+ return nil
+ }
+ return (*listNode)(unsafe.Add(p, head.nodeOffset))
+}
+
+// Returns true if the list is empty.
+func (head *listHead) empty() bool {
+ return head.obj == nil
+}
+
+// Returns the head of the list without removing it.
+func (head *listHead) head() unsafe.Pointer {
+ return head.obj
+}
+
+// Push p onto the front of the list.
+func (head *listHead) push(p unsafe.Pointer) {
+ // p becomes the head of the list.
+
+ // ... so p's next is the current head.
+ pNode := head.getNode(p)
+ pNode.next = head.obj
+
+ // ... and the current head's prev is p.
+ if head.obj != nil {
+ headNode := head.getNode(head.obj)
+ headNode.prev = p
+ }
+
+ head.obj = p
+}
+
+// Pop removes the head of the list.
+func (head *listHead) pop() unsafe.Pointer {
+ if head.obj == nil {
+ return nil
+ }
+
+ // Return the head of the list.
+ p := head.obj
+
+ // ... so the new head is p's next.
+ pNode := head.getNode(p)
+ head.obj = pNode.next
+ // p is no longer on the list. Clear next to remove unused references.
+ // N.B. as the head, prev must already be nil.
+ pNode.next = nil
+
+ // ... and the new head no longer has a prev.
+ if head.obj != nil {
+ headNode := head.getNode(head.obj)
+ headNode.prev = nil
+ }
+
+ return p
+}
+
+// Remove p from the middle of the list.
+func (head *listHead) remove(p unsafe.Pointer) {
+ if head.obj == p {
+ // Use pop to ensure head is updated when removing the head.
+ head.pop()
+ return
+ }
+
+ pNode := head.getNode(p)
+ prevNode := head.getNode(pNode.prev)
+ nextNode := head.getNode(pNode.next)
+
+ // Link prev to next.
+ if prevNode != nil {
+ prevNode.next = pNode.next
+ }
+ // Link next to prev.
+ if nextNode != nil {
+ nextNode.prev = pNode.prev
+ }
+
+ pNode.prev = nil
+ pNode.next = nil
+}
--- /dev/null
+// 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
+
+import (
+ "unsafe"
+)
+
+// The types in this file are exact copies of the types in list.go, but with
+// unsafe.Pointer replaced with uintptr for use where write barriers must be
+// avoided, such as uses of muintptr, puintptr, guintptr.
+//
+// Objects in these lists must be kept alive via another real reference.
+
+// listHeadManual points to the head of an intrusive doubly-linked list of
+// objects.
+//
+// Prior to use, you must call init to store the offset of listNodeManual fields.
+//
+// Every object in the list should be the same type.
+type listHeadManual struct {
+ obj uintptr
+
+ initialized bool
+ nodeOffset uintptr
+}
+
+// init initializes the list head. off is the offset (via unsafe.Offsetof) of
+// the listNodeManual field in the objects in the list.
+func (head *listHeadManual) init(off uintptr) {
+ head.initialized = true
+ head.nodeOffset = off
+}
+
+// listNodeManual is the linked list node for objects in a listHeadManual list.
+//
+// listNodeManual must be stored as a field in objects placed in the linked list.
+// The offset of the field is registered via listHeadManual.init.
+//
+// For example:
+//
+// type foo struct {
+// val int
+//
+// node listNodeManual
+// }
+//
+// var fooHead listHeadManual
+// fooHead.init(unsafe.Offsetof(foo{}.node))
+type listNodeManual struct {
+ prev uintptr
+ next uintptr
+}
+
+func (head *listHeadManual) getNode(p unsafe.Pointer) *listNodeManual {
+ if !head.initialized {
+ throw("runtime: uninitialized listHead")
+ }
+
+ if p == nil {
+ return nil
+ }
+ return (*listNodeManual)(unsafe.Add(p, head.nodeOffset))
+}
+
+// Returns true if the list is empty.
+func (head *listHeadManual) empty() bool {
+ return head.obj == 0
+}
+
+// Returns the head of the list without removing it.
+func (head *listHeadManual) head() unsafe.Pointer {
+ return unsafe.Pointer(head.obj)
+}
+
+// Push p onto the front of the list.
+func (head *listHeadManual) push(p unsafe.Pointer) {
+ // p becomes the head of the list.
+
+ // ... so p's next is the current head.
+ pNode := head.getNode(p)
+ pNode.next = head.obj
+
+ // ... and the current head's prev is p.
+ if head.obj != 0 {
+ headNode := head.getNode(unsafe.Pointer(head.obj))
+ headNode.prev = uintptr(p)
+ }
+
+ head.obj = uintptr(p)
+}
+
+// Pop removes the head of the list.
+func (head *listHeadManual) pop() unsafe.Pointer {
+ if head.obj == 0 {
+ return nil
+ }
+
+ // Return the head of the list.
+ p := unsafe.Pointer(head.obj)
+
+ // ... so the new head is p's next.
+ pNode := head.getNode(p)
+ head.obj = pNode.next
+ // p is no longer on the list. Clear next to remove unused references.
+ // N.B. as the head, prev must already be nil.
+ pNode.next = 0
+
+ // ... and the new head no longer has a prev.
+ if head.obj != 0 {
+ headNode := head.getNode(unsafe.Pointer(head.obj))
+ headNode.prev = 0
+ }
+
+ return p
+}
+
+// Remove p from the middle of the list.
+func (head *listHeadManual) remove(p unsafe.Pointer) {
+ if unsafe.Pointer(head.obj) == p {
+ // Use pop to ensure head is updated when removing the head.
+ head.pop()
+ return
+ }
+
+ pNode := head.getNode(p)
+ prevNode := head.getNode(unsafe.Pointer(pNode.prev))
+ nextNode := head.getNode(unsafe.Pointer(pNode.next))
+
+ // Link prev to next.
+ if prevNode != nil {
+ prevNode.next = pNode.next
+ }
+ // Link next to prev.
+ if nextNode != nil {
+ nextNode.prev = pNode.prev
+ }
+
+ pNode.prev = 0
+ pNode.next = 0
+}
--- /dev/null
+// 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
+}
+
+func newListedValManual(v int) *listedValManual {
+ return &listedValManual{
+ val: v,
+ }
+}
+
+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 := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(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 := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(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 := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(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 := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(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 := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(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)
+ }
+}
--- /dev/null
+// 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 (
+ "runtime"
+ "testing"
+ "unsafe"
+)
+
+type listedVal struct {
+ val int
+
+ aNode runtime.ListNode
+ bNode runtime.ListNode
+}
+
+func newListedVal(v int) *listedVal {
+ return &listedVal{
+ val: v,
+ }
+}
+
+func TestListPush(t *testing.T) {
+ var headA runtime.ListHead
+ headA.Init(unsafe.Offsetof(listedVal{}.aNode))
+
+ one := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(3)
+ headA.Push(unsafe.Pointer(three))
+
+ p := headA.Pop()
+ v := (*listedVal)(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 = (*listedVal)(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 = (*listedVal)(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 = (*listedVal)(p)
+ if v != nil {
+ t.Fatalf("pop got %+v want nil", v)
+ }
+}
+
+func wantVal(t *testing.T, v *listedVal, 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 TestListRemoveHead(t *testing.T) {
+ var headA runtime.ListHead
+ headA.Init(unsafe.Offsetof(listedVal{}.aNode))
+
+ one := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(3)
+ headA.Push(unsafe.Pointer(three))
+
+ headA.Remove(unsafe.Pointer(three))
+
+ p := headA.Pop()
+ v := (*listedVal)(p)
+ wantVal(t, v, 2)
+
+ p = headA.Pop()
+ v = (*listedVal)(p)
+ wantVal(t, v, 1)
+
+ p = headA.Pop()
+ v = (*listedVal)(p)
+ if v != nil {
+ t.Fatalf("pop got %+v want nil", v)
+ }
+}
+
+func TestListRemoveMiddle(t *testing.T) {
+ var headA runtime.ListHead
+ headA.Init(unsafe.Offsetof(listedVal{}.aNode))
+
+ one := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(3)
+ headA.Push(unsafe.Pointer(three))
+
+ headA.Remove(unsafe.Pointer(two))
+
+ p := headA.Pop()
+ v := (*listedVal)(p)
+ wantVal(t, v, 3)
+
+ p = headA.Pop()
+ v = (*listedVal)(p)
+ wantVal(t, v, 1)
+
+ p = headA.Pop()
+ v = (*listedVal)(p)
+ if v != nil {
+ t.Fatalf("pop got %+v want nil", v)
+ }
+}
+
+func TestListRemoveTail(t *testing.T) {
+ var headA runtime.ListHead
+ headA.Init(unsafe.Offsetof(listedVal{}.aNode))
+
+ one := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(3)
+ headA.Push(unsafe.Pointer(three))
+
+ headA.Remove(unsafe.Pointer(one))
+
+ p := headA.Pop()
+ v := (*listedVal)(p)
+ wantVal(t, v, 3)
+
+ p = headA.Pop()
+ v = (*listedVal)(p)
+ wantVal(t, v, 2)
+
+ p = headA.Pop()
+ v = (*listedVal)(p)
+ if v != nil {
+ t.Fatalf("pop got %+v want nil", v)
+ }
+}
+
+func TestListRemoveAll(t *testing.T) {
+ var headA runtime.ListHead
+ headA.Init(unsafe.Offsetof(listedVal{}.aNode))
+
+ one := newListedVal(1)
+ headA.Push(unsafe.Pointer(one))
+
+ two := newListedVal(2)
+ headA.Push(unsafe.Pointer(two))
+
+ three := newListedVal(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 := (*listedVal)(p)
+ if v != nil {
+ t.Fatalf("pop got %+v want nil", v)
+ }
+}
+
+func BenchmarkListPushPop(b *testing.B) {
+ var head runtime.ListHead
+ head.Init(unsafe.Offsetof(listedVal{}.aNode))
+
+ vals := make([]listedVal, 10000)
+ i := 0
+ for b.Loop() {
+ if i == len(vals) {
+ for range len(vals) {
+ head.Pop()
+ }
+ i = 0
+ }
+
+ head.Push(unsafe.Pointer(&vals[i]))
+
+ i++
+ }
+}