go/mqtt/internal/appendable_list_with_removal.go (60 lines of code) (raw):
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
package internal
import (
"iter"
"sync"
)
type listNode[T any] struct {
value T
prev *listNode[T]
next *listNode[T]
}
type AppendableListWithRemoval[T any] struct {
mu sync.RWMutex
first *listNode[T]
last *listNode[T]
}
func NewAppendableListWithRemoval[T any]() *AppendableListWithRemoval[T] {
return &AppendableListWithRemoval[T]{}
}
func (l *AppendableListWithRemoval[T]) AppendEntry(
value T,
) (removeEntry func()) {
l.mu.Lock()
defer l.mu.Unlock()
node := &listNode[T]{value: value}
if l.last == nil {
l.first = node
} else {
l.last.next = node
}
node.prev = l.last
l.last = node
return func() {
l.mu.Lock()
defer l.mu.Unlock()
if node == nil {
// Node was already deleted.
return
}
if node.prev == nil {
l.first = node.next
} else {
node.prev.next = node.next
}
if node.next == nil {
l.last = node.prev
} else {
node.next.prev = node.prev
}
// Set this to nil so the node can be garbage collected.
node = nil
}
}
func (l *AppendableListWithRemoval[T]) All() iter.Seq[T] {
return func(yield func(T) bool) {
l.mu.RLock()
defer l.mu.RUnlock()
curr := l.first
for curr != nil && yield(curr.value) {
curr = curr.next
}
}
}