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