Skip to content

Commit

Permalink
internal/xlist changes
Browse files Browse the repository at this point in the history
  • Loading branch information
asmyasnikov committed Sep 6, 2024
1 parent cd58c33 commit c269883
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 85 deletions.
4 changes: 2 additions & 2 deletions internal/table/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ type Client struct {
index map[*session]sessionInfo
createInProgress int // KIKIMR-9163: in-create-process counter
limit int // Upper bound for Client size.
idle *xlist.List[*session]
waitQ *xlist.List[*chan *session]
idle xlist.List[*session]
waitQ xlist.List[*chan *session]
waitChPool sync.Pool
testHookGetWaitCh func() // nil except some tests.
wg sync.WaitGroup
Expand Down
137 changes: 97 additions & 40 deletions internal/xlist/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type Element[T any] struct {
next, prev *Element[T]

// The list to which this element belongs.
list *List[T]
list *list[T]

// The value stored with this element.
Value T
Expand All @@ -38,29 +38,99 @@ func (e *Element[T]) Prev() *Element[T] {

// List represents a doubly linked list.
// The zero value for List is an empty list ready to use.
type List[T any] struct {
root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
type (
List[T any] interface {
// PushBack inserts a new element e with value v at the back of list l and returns e.
PushBack(v T) *Element[T]

// PushBackList inserts a copy of another list at the back of list l.
// The lists l and other may be the same. They must not be nil.
PushBackList(other List[T])

// PushFront inserts a new element e with value v at the front of list l and returns e.
PushFront(v T) *Element[T]

// PushFrontList inserts a copy of another list at the front of list l.
// The lists l and other may be the same. They must not be nil.
PushFrontList(other List[T])

// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
MoveToFront(e *Element[T])

// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
MoveToBack(e *Element[T])

// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
MoveAfter(e, mark *Element[T])

// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
MoveBefore(e, mark *Element[T])

// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
Remove(e *Element[T]) T

// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
InsertBefore(v T, mark *Element[T]) *Element[T]

// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
InsertAfter(v T, mark *Element[T]) *Element[T]

// Front returns the first element of list l or nil if the list is empty.
Front() *Element[T]

// Back returns the last element of list l or nil if the list is empty.
Back() *Element[T]

// Len returns the number of elements of list l.
// The complexity is O(1).
Len() int

// Clear clears the list l.
Clear()
}
list[T any] struct {
root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}
)

// Clear clears the list l.
func (l *list[T]) Clear() {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
}

// Init initializes or clears list l.
func (l *List[T]) Init() *List[T] {
// New returns an initialized list.
func New[T any]() List[T] {
l := &list[T]{}
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0

return l
}

// New returns an initialized list.
func New[T any]() *List[T] { return new(List[T]).Init() }

// Len returns the number of elements of list l.
// The complexity is O(1).
func (l *List[T]) Len() int { return l.len }
func (l *list[T]) Len() int { return l.len }

// Front returns the first element of list l or nil if the list is empty.
func (l *List[T]) Front() *Element[T] {
func (l *list[T]) Front() *Element[T] {
if l.len == 0 {
return nil
}
Expand All @@ -69,23 +139,16 @@ func (l *List[T]) Front() *Element[T] {
}

// Back returns the last element of list l or nil if the list is empty.
func (l *List[T]) Back() *Element[T] {
func (l *list[T]) Back() *Element[T] {
if l.len == 0 {
return nil
}

return l.root.prev
}

// lazyInit lazily initializes a zero List value.
func (l *List[T]) lazyInit() {
if l.root.next == nil {
l.Init()
}
}

// insert inserts e after at, increments l.len, and returns e.
func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
func (l *list[T]) insert(e, at *Element[T]) *Element[T] {
e.prev = at
e.next = at.next
e.prev.next = e
Expand All @@ -97,12 +160,12 @@ func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
}

// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] {
func (l *list[T]) insertValue(v T, at *Element[T]) *Element[T] {
return l.insert(&Element[T]{Value: v}, at)
}

// remove removes e from its list, decrements l.len
func (l *List[T]) remove(e *Element[T]) {
func (l *list[T]) remove(e *Element[T]) {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil // avoid memory leaks
Expand All @@ -112,7 +175,7 @@ func (l *List[T]) remove(e *Element[T]) {
}

// move moves e to next to at.
func (l *List[T]) move(e, at *Element[T]) {
func (l *list[T]) move(e, at *Element[T]) {
if e == at {
return
}
Expand All @@ -128,7 +191,7 @@ func (l *List[T]) move(e, at *Element[T]) {
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
func (l *List[T]) Remove(e *Element[T]) T {
func (l *list[T]) Remove(e *Element[T]) T {
if e.list == l {
// if e.list == l, l must have been initialized when e was inserted
// in l or l == nil (e is a zero Element) and l.remove will crash
Expand All @@ -139,23 +202,19 @@ func (l *List[T]) Remove(e *Element[T]) T {
}

// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *List[T]) PushFront(v T) *Element[T] {
l.lazyInit()

func (l *list[T]) PushFront(v T) *Element[T] {
return l.insertValue(v, &l.root)
}

// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *List[T]) PushBack(v T) *Element[T] {
l.lazyInit()

func (l *list[T]) PushBack(v T) *Element[T] {
return l.insertValue(v, l.root.prev)
}

// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
func (l *list[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
if mark.list != l {
return nil
}
Expand All @@ -167,7 +226,7 @@ func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
func (l *list[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
if mark.list != l {
return nil
}
Expand All @@ -179,7 +238,7 @@ func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List[T]) MoveToFront(e *Element[T]) {
func (l *list[T]) MoveToFront(e *Element[T]) {
if e.list != l || l.root.next == e {
return
}
Expand All @@ -190,7 +249,7 @@ func (l *List[T]) MoveToFront(e *Element[T]) {
// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *List[T]) MoveToBack(e *Element[T]) {
func (l *list[T]) MoveToBack(e *Element[T]) {
if e.list != l || l.root.prev == e {
return
}
Expand All @@ -201,7 +260,7 @@ func (l *List[T]) MoveToBack(e *Element[T]) {
// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List[T]) MoveBefore(e, mark *Element[T]) {
func (l *list[T]) MoveBefore(e, mark *Element[T]) {
if e.list != l || e == mark || mark.list != l {
return
}
Expand All @@ -211,7 +270,7 @@ func (l *List[T]) MoveBefore(e, mark *Element[T]) {
// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *List[T]) MoveAfter(e, mark *Element[T]) {
func (l *list[T]) MoveAfter(e, mark *Element[T]) {
if e.list != l || e == mark || mark.list != l {
return
}
Expand All @@ -220,17 +279,15 @@ func (l *List[T]) MoveAfter(e, mark *Element[T]) {

// PushBackList inserts a copy of another list at the back of list l.
// The lists l and other may be the same. They must not be nil.
func (l *List[T]) PushBackList(other *List[T]) {
l.lazyInit()
func (l *list[T]) PushBackList(other List[T]) {
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
l.insertValue(e.Value, l.root.prev)
}
}

// PushFrontList inserts a copy of another list at the front of list l.
// The lists l and other may be the same. They must not be nil.
func (l *List[T]) PushFrontList(other *List[T]) {
l.lazyInit()
func (l *list[T]) PushFrontList(other List[T]) {
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
l.insertValue(e.Value, &l.root)
}
Expand Down
61 changes: 18 additions & 43 deletions internal/xlist/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package xlist

import "testing"

func checkListLen[T comparable](t *testing.T, l *List[T], expectedLen int) bool {
func checkListLen[T comparable](t *testing.T, l List[T], expectedLen int) bool {
if n := l.Len(); n != expectedLen {
t.Errorf("l.Len() = %d, want %d", n, expectedLen)

Expand All @@ -12,17 +12,19 @@ func checkListLen[T comparable](t *testing.T, l *List[T], expectedLen int) bool
return true
}

func checkListPointers[T comparable](t *testing.T, l *List[T], es []*Element[T]) {
root := &l.root
func checkListPointers[T comparable](t *testing.T, l List[T], es []*Element[T]) {
root := &l.(*list[T]).root

if !checkListLen(t, l, len(es)) {
return
}

// zero length lists must be the zero value or properly initialized (sentinel circle)
if len(es) == 0 {
if l.root.next != nil && l.root.next != root || l.root.prev != nil && l.root.prev != root {
t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p", l.root.next, l.root.prev, root)
if l.(*list[T]).root.next != nil && l.(*list[T]).root.next != root ||
l.(*list[T]).root.prev != nil && l.(*list[T]).root.prev != root {
t.Errorf("l.root.next = %p, l.root.prev = %p; both should both be nil or %p",
l.(*list[T]).root.next, l.(*list[T]).root.prev, root)
}

return
Expand Down Expand Up @@ -145,7 +147,7 @@ func TestList(t *testing.T) {
})
}

func checkList[T comparable](t *testing.T, l *List[T], es []any) {
func checkList[T comparable](t *testing.T, l List[T], es []any) {
if !checkListLen(t, l, len(es)) {
return
}
Expand Down Expand Up @@ -289,66 +291,39 @@ func TestMove(t *testing.T) {
checkListPointers(t, l, []*Element[int]{e1, e3, e2, e4})
}

// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List[T]
func TestZeroList(t *testing.T) {
t.Run("PushFront", func(t *testing.T) {
l1 := new(List[int])
l1.PushFront(1)
checkList(t, l1, []any{1})

t.Run("PushFrontList", func(t *testing.T) {
l3 := new(List[int])
l3.PushFrontList(l1)
checkList(t, l3, []any{1})
})
})

t.Run("PushBack", func(t *testing.T) {
l2 := new(List[int])
l2.PushBack(1)
checkList(t, l2, []any{1})

t.Run("PushBackList", func(t *testing.T) {
l4 := new(List[int])
l4.PushBackList(l2)
checkList(t, l4, []any{1})
})
})
}

// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
func TestInsertBeforeUnknownMark(t *testing.T) {
var l List[int]
l := New[int]()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(1, new(Element[int]))
checkList(t, &l, []any{1, 2, 3})
checkList(t, l, []any{1, 2, 3})
}

// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
func TestInsertAfterUnknownMark(t *testing.T) {
var l List[int]
l := New[int]()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(1, new(Element[int]))
checkList(t, &l, []any{1, 2, 3})
checkList(t, l, []any{1, 2, 3})
}

// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
func TestMoveUnknownMark(t *testing.T) {
var l1 List[int]
l1 := New[int]()
e1 := l1.PushBack(1)

var l2 List[int]
l2 := New[int]()
e2 := l2.PushBack(2)

l1.MoveAfter(e1, e2)
checkList(t, &l1, []any{1})
checkList(t, &l2, []any{2})
checkList(t, l1, []any{1})
checkList(t, l2, []any{2})

l1.MoveBefore(e1, e2)
checkList(t, &l1, []any{1})
checkList(t, &l2, []any{2})
checkList(t, l1, []any{1})
checkList(t, l2, []any{2})
}
Loading

0 comments on commit c269883

Please sign in to comment.