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