Skip to content

Commit a424af4

Browse files
authored
Merge pull request #1329 from ydb-platform/xlist
Added internal/xlist + switched internal/table.Client from container/list to internal/xlist
2 parents 2926c43 + 06b7c55 commit a424af4

File tree

4 files changed

+607
-32
lines changed

4 files changed

+607
-32
lines changed

internal/table/client.go

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package table
22

33
import (
4-
"container/list"
54
"context"
65
"fmt"
76
"sync"
@@ -15,6 +14,7 @@ import (
1514
"github.com/ydb-platform/ydb-go-sdk/v3/internal/table/config"
1615
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xcontext"
1716
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors"
17+
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xlist"
1818
"github.com/ydb-platform/ydb-go-sdk/v3/internal/xsync"
1919
"github.com/ydb-platform/ydb-go-sdk/v3/meta"
2020
"github.com/ydb-platform/ydb-go-sdk/v3/retry"
@@ -50,8 +50,8 @@ func newClient(
5050
cc: cc,
5151
build: builder,
5252
index: make(map[*session]sessionInfo),
53-
idle: list.New(),
54-
waitQ: list.New(),
53+
idle: xlist.New[*session](),
54+
waitQ: xlist.New[*chan *session](),
5555
limit: config.SizeLimit(),
5656
waitChPool: sync.Pool{
5757
New: func() interface{} {
@@ -82,10 +82,10 @@ type Client struct {
8282
// read-write fields
8383
mu xsync.Mutex
8484
index map[*session]sessionInfo
85-
createInProgress int // KIKIMR-9163: in-create-process counter
86-
limit int // Upper bound for Client size.
87-
idle *list.List // list<*session>
88-
waitQ *list.List // list<*chan *session>
85+
createInProgress int // KIKIMR-9163: in-create-process counter
86+
limit int // Upper bound for Client size.
87+
idle *xlist.List[*session]
88+
waitQ *xlist.List[*chan *session]
8989
waitChPool sync.Pool
9090
testHookGetWaitCh func() // nil except some tests.
9191
wg sync.WaitGroup
@@ -484,7 +484,7 @@ func (c *Client) Get(ctx context.Context) (s *session, err error) {
484484
func (c *Client) internalPoolWaitFromCh(ctx context.Context, t *trace.Table) (s *session, err error) {
485485
var (
486486
ch *chan *session
487-
el *list.Element // Element in the wait queue.
487+
el *xlist.Element[*chan *session] // Element in the wait queue.
488488
ok bool
489489
)
490490

@@ -626,18 +626,11 @@ func (c *Client) Close(ctx context.Context) (err error) {
626626
c.limit = 0
627627

628628
for el := c.waitQ.Front(); el != nil; el = el.Next() {
629-
ch, ok := el.Value.(*chan *session)
630-
if !ok {
631-
panic(fmt.Sprintf("unsupported type conversion from %T to *chan *session", ch))
632-
}
633-
close(*ch)
629+
close(*el.Value)
634630
}
635631

636632
for e := c.idle.Front(); e != nil; e = e.Next() {
637-
s, ok := e.Value.(*session)
638-
if !ok {
639-
panic(fmt.Sprintf("unsupported type conversion from %T to *session", s))
640-
}
633+
s := e.Value
641634
s.SetStatus(table.SessionClosing)
642635
c.wg.Add(1)
643636
go func() {
@@ -765,10 +758,7 @@ func (c *Client) internalPoolGCTick(ctx context.Context, idleThreshold time.Dura
765758
return
766759
}
767760
for e := c.idle.Front(); e != nil; e = e.Next() {
768-
s, ok := e.Value.(*session)
769-
if !ok {
770-
panic(fmt.Sprintf("unsupported type conversion from %T to *session", s))
771-
}
761+
s := e.Value
772762
info, has := c.index[s]
773763
if !has {
774764
panic("session not found in pool")
@@ -841,10 +831,7 @@ func (c *Client) internalPoolPeekFirstIdle() (s *session, touched time.Time) {
841831
if el == nil {
842832
return
843833
}
844-
s, ok := el.Value.(*session)
845-
if !ok {
846-
panic(fmt.Sprintf("unsupported type conversion from %T to *session", s))
847-
}
834+
s = el.Value
848835
info, has := c.index[s]
849836
if !has || el != info.idle {
850837
panic("inconsistent session client index")
@@ -883,10 +870,7 @@ func (c *Client) internalPoolNotify(s *session) (notified bool) {
883870
// missed something and may want to retry (especially for case (3)).
884871
//
885872
// After that we taking a next waiter and repeat the same.
886-
ch, ok := c.waitQ.Remove(el).(*chan *session)
887-
if !ok {
888-
panic(fmt.Sprintf("unsupported type conversion from %T to *chan *session", ch))
889-
}
873+
ch := c.waitQ.Remove(el)
890874
select {
891875
case *ch <- s:
892876
// Case (1).
@@ -933,7 +917,7 @@ func (c *Client) internalPoolPushIdle(s *session, now time.Time) {
933917
}
934918

935919
// c.mu must be held.
936-
func (c *Client) internalPoolHandlePushIdle(s *session, now time.Time, el *list.Element) {
920+
func (c *Client) internalPoolHandlePushIdle(s *session, now time.Time, el *xlist.Element[*session]) {
937921
info, has := c.index[s]
938922
if !has {
939923
panic("trying to store session created outside of the client")
@@ -948,6 +932,6 @@ func (c *Client) internalPoolHandlePushIdle(s *session, now time.Time, el *list.
948932
}
949933

950934
type sessionInfo struct {
951-
idle *list.Element
935+
idle *xlist.Element[*session]
952936
touched time.Time
953937
}

internal/table/client_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -912,7 +912,7 @@ func (s *StubBuilder) createSession(ctx context.Context) (session *session, err
912912
func (c *Client) debug() {
913913
fmt.Print("head ")
914914
for el := c.idle.Front(); el != nil; el = el.Next() {
915-
s := el.Value.(*session)
915+
s := el.Value
916916
x := c.index[s]
917917
fmt.Printf("<-> %s(%d) ", s.ID(), x.touched.Unix())
918918
}

internal/xlist/list.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// Package xlist is a copy of standard container/list but uses generics
2+
// for strict checks on compile time
3+
package xlist
4+
5+
// Element is an element of a linked list.
6+
type Element[T any] struct {
7+
// Next and previous pointers in the doubly-linked list of elements.
8+
// To simplify the implementation, internally a list l is implemented
9+
// as a ring, such that &l.root is both the next element of the last
10+
// list element (l.Back()) and the previous element of the first list
11+
// element (l.Front()).
12+
next, prev *Element[T]
13+
14+
// The list to which this element belongs.
15+
list *List[T]
16+
17+
// The value stored with this element.
18+
Value T
19+
}
20+
21+
// Next returns the next list element or nil.
22+
func (e *Element[T]) Next() *Element[T] {
23+
if p := e.next; e.list != nil && p != &e.list.root {
24+
return p
25+
}
26+
27+
return nil
28+
}
29+
30+
// Prev returns the previous list element or nil.
31+
func (e *Element[T]) Prev() *Element[T] {
32+
if p := e.prev; e.list != nil && p != &e.list.root {
33+
return p
34+
}
35+
36+
return nil
37+
}
38+
39+
// List represents a doubly linked list.
40+
// The zero value for List is an empty list ready to use.
41+
type List[T any] struct {
42+
root Element[T] // sentinel list element, only &root, root.prev, and root.next are used
43+
len int // current list length excluding (this) sentinel element
44+
}
45+
46+
// Init initializes or clears list l.
47+
func (l *List[T]) Init() *List[T] {
48+
l.root.next = &l.root
49+
l.root.prev = &l.root
50+
l.len = 0
51+
52+
return l
53+
}
54+
55+
// New returns an initialized list.
56+
func New[T any]() *List[T] { return new(List[T]).Init() }
57+
58+
// Len returns the number of elements of list l.
59+
// The complexity is O(1).
60+
func (l *List[T]) Len() int { return l.len }
61+
62+
// Front returns the first element of list l or nil if the list is empty.
63+
func (l *List[T]) Front() *Element[T] {
64+
if l.len == 0 {
65+
return nil
66+
}
67+
68+
return l.root.next
69+
}
70+
71+
// Back returns the last element of list l or nil if the list is empty.
72+
func (l *List[T]) Back() *Element[T] {
73+
if l.len == 0 {
74+
return nil
75+
}
76+
77+
return l.root.prev
78+
}
79+
80+
// lazyInit lazily initializes a zero List value.
81+
func (l *List[T]) lazyInit() {
82+
if l.root.next == nil {
83+
l.Init()
84+
}
85+
}
86+
87+
// insert inserts e after at, increments l.len, and returns e.
88+
func (l *List[T]) insert(e, at *Element[T]) *Element[T] {
89+
e.prev = at
90+
e.next = at.next
91+
e.prev.next = e
92+
e.next.prev = e
93+
e.list = l
94+
l.len++
95+
96+
return e
97+
}
98+
99+
// insertValue is a convenience wrapper for insert(&Element{Value: v}, at).
100+
func (l *List[T]) insertValue(v T, at *Element[T]) *Element[T] {
101+
return l.insert(&Element[T]{Value: v}, at)
102+
}
103+
104+
// remove removes e from its list, decrements l.len
105+
func (l *List[T]) remove(e *Element[T]) {
106+
e.prev.next = e.next
107+
e.next.prev = e.prev
108+
e.next = nil // avoid memory leaks
109+
e.prev = nil // avoid memory leaks
110+
e.list = nil
111+
l.len--
112+
}
113+
114+
// move moves e to next to at.
115+
func (l *List[T]) move(e, at *Element[T]) {
116+
if e == at {
117+
return
118+
}
119+
e.prev.next = e.next
120+
e.next.prev = e.prev
121+
122+
e.prev = at
123+
e.next = at.next
124+
e.prev.next = e
125+
e.next.prev = e
126+
}
127+
128+
// Remove removes e from l if e is an element of list l.
129+
// It returns the element value e.Value.
130+
// The element must not be nil.
131+
func (l *List[T]) Remove(e *Element[T]) T {
132+
if e.list == l {
133+
// if e.list == l, l must have been initialized when e was inserted
134+
// in l or l == nil (e is a zero Element) and l.remove will crash
135+
l.remove(e)
136+
}
137+
138+
return e.Value
139+
}
140+
141+
// PushFront inserts a new element e with value v at the front of list l and returns e.
142+
func (l *List[T]) PushFront(v T) *Element[T] {
143+
l.lazyInit()
144+
145+
return l.insertValue(v, &l.root)
146+
}
147+
148+
// PushBack inserts a new element e with value v at the back of list l and returns e.
149+
func (l *List[T]) PushBack(v T) *Element[T] {
150+
l.lazyInit()
151+
152+
return l.insertValue(v, l.root.prev)
153+
}
154+
155+
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
156+
// If mark is not an element of l, the list is not modified.
157+
// The mark must not be nil.
158+
func (l *List[T]) InsertBefore(v T, mark *Element[T]) *Element[T] {
159+
if mark.list != l {
160+
return nil
161+
}
162+
163+
// see comment in List.Remove about initialization of l
164+
return l.insertValue(v, mark.prev)
165+
}
166+
167+
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
168+
// If mark is not an element of l, the list is not modified.
169+
// The mark must not be nil.
170+
func (l *List[T]) InsertAfter(v T, mark *Element[T]) *Element[T] {
171+
if mark.list != l {
172+
return nil
173+
}
174+
175+
// see comment in List.Remove about initialization of l
176+
return l.insertValue(v, mark)
177+
}
178+
179+
// MoveToFront moves element e to the front of list l.
180+
// If e is not an element of l, the list is not modified.
181+
// The element must not be nil.
182+
func (l *List[T]) MoveToFront(e *Element[T]) {
183+
if e.list != l || l.root.next == e {
184+
return
185+
}
186+
// see comment in List.Remove about initialization of l
187+
l.move(e, &l.root)
188+
}
189+
190+
// MoveToBack moves element e to the back of list l.
191+
// If e is not an element of l, the list is not modified.
192+
// The element must not be nil.
193+
func (l *List[T]) MoveToBack(e *Element[T]) {
194+
if e.list != l || l.root.prev == e {
195+
return
196+
}
197+
// see comment in List.Remove about initialization of l
198+
l.move(e, l.root.prev)
199+
}
200+
201+
// MoveBefore moves element e to its new position before mark.
202+
// If e or mark is not an element of l, or e == mark, the list is not modified.
203+
// The element and mark must not be nil.
204+
func (l *List[T]) MoveBefore(e, mark *Element[T]) {
205+
if e.list != l || e == mark || mark.list != l {
206+
return
207+
}
208+
l.move(e, mark.prev)
209+
}
210+
211+
// MoveAfter moves element e to its new position after mark.
212+
// If e or mark is not an element of l, or e == mark, the list is not modified.
213+
// The element and mark must not be nil.
214+
func (l *List[T]) MoveAfter(e, mark *Element[T]) {
215+
if e.list != l || e == mark || mark.list != l {
216+
return
217+
}
218+
l.move(e, mark)
219+
}
220+
221+
// PushBackList inserts a copy of another list at the back of list l.
222+
// The lists l and other may be the same. They must not be nil.
223+
func (l *List[T]) PushBackList(other *List[T]) {
224+
l.lazyInit()
225+
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
226+
l.insertValue(e.Value, l.root.prev)
227+
}
228+
}
229+
230+
// PushFrontList inserts a copy of another list at the front of list l.
231+
// The lists l and other may be the same. They must not be nil.
232+
func (l *List[T]) PushFrontList(other *List[T]) {
233+
l.lazyInit()
234+
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
235+
l.insertValue(e.Value, &l.root)
236+
}
237+
}

0 commit comments

Comments
 (0)