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