blob: f0ce9ad4da3efe6ad6b2882703f43200690089ef [file] [log] [blame]
// 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
}