Skip to content

Commit 04f922d

Browse files
committed
Remove list implementation from record
1 parent 5fb8262 commit 04f922d

File tree

5 files changed

+66
-104
lines changed

5 files changed

+66
-104
lines changed

backend.go

+40-36
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,24 @@ package evcache
22

33
import (
44
"sync"
5+
"sync/atomic"
56
"time"
67

78
"github.com/mgnsk/list"
89
)
910

10-
type recordList[V any] struct {
11-
list.ListOf[record[V], *record[V]]
11+
type record[V any] struct {
12+
value V
13+
deadline int64
14+
wg sync.WaitGroup
15+
initialized atomic.Bool
1216
}
1317

1418
type backend[K comparable, V any] struct {
1519
timer *time.Timer
1620
done chan struct{}
17-
xmap backendMap[K, *record[V]]
18-
list recordList[V]
21+
xmap backendMap[K, *list.Element[record[V]]]
22+
list list.List[record[V]]
1923
earliestExpireAt int64
2024
cap int
2125
once sync.Once
@@ -29,7 +33,7 @@ func newBackend[K comparable, V any](capacity int) *backend[K, V] {
2933
return &backend[K, V]{
3034
timer: t,
3135
done: make(chan struct{}),
32-
xmap: make(builtinMap[K, *record[V]], capacity),
36+
xmap: make(builtinMap[K, *list.Element[record[V]]], capacity),
3337
cap: capacity,
3438
}
3539
}
@@ -39,66 +43,66 @@ func (b *backend[K, V]) close() error {
3943
return nil
4044
}
4145

42-
func (b *backend[K, V]) LoadOrStore(key K, new *record[V]) (old *record[V], loaded bool) {
46+
func (b *backend[K, V]) LoadOrStore(key K, new *list.Element[record[V]]) (old *list.Element[record[V]], loaded bool) {
4347
b.mu.RLock()
44-
if r, ok := b.xmap.Load(key); ok {
48+
if elem, ok := b.xmap.Load(key); ok {
4549
b.mu.RUnlock()
46-
return r, true
50+
return elem, true
4751
}
4852
b.mu.RUnlock()
4953

5054
b.mu.Lock()
5155
defer b.mu.Unlock()
5256

53-
if r, ok := b.xmap.Load(key); ok {
54-
return r, true
57+
if elem, ok := b.xmap.Load(key); ok {
58+
return elem, true
5559
}
5660

5761
b.xmap.Store(key, new)
5862

5963
return new, false
6064
}
6165

62-
func (b *backend[K, V]) Range(f func(key K, r *record[V]) bool) {
66+
func (b *backend[K, V]) Range(f func(key K, r *list.Element[record[V]]) bool) {
6367
b.mu.RLock()
6468
keys := make([]K, 0, b.xmap.Len())
65-
b.xmap.Range(func(key K, _ *record[V]) bool {
69+
b.xmap.Range(func(key K, _ *list.Element[record[V]]) bool {
6670
keys = append(keys, key)
6771
return true
6872
})
6973
b.mu.RUnlock()
7074

7175
for _, key := range keys {
7276
b.mu.RLock()
73-
r, ok := b.xmap.Load(key)
77+
elem, ok := b.xmap.Load(key)
7478
b.mu.RUnlock()
75-
if ok && !f(key, r) {
79+
if ok && !f(key, elem) {
7680
return
7781
}
7882
}
7983
}
8084

81-
func (b *backend[K, V]) evict(key K) (*record[V], bool) {
82-
if r, ok := b.xmap.Load(key); ok && r.initialized.Load() {
85+
func (b *backend[K, V]) evict(key K) (*list.Element[record[V]], bool) {
86+
if elem, ok := b.xmap.Load(key); ok && elem.Value.initialized.Load() {
8387
b.xmap.Delete(key)
84-
b.list.Remove(r)
85-
return r, true
88+
b.list.Remove(elem)
89+
return elem, true
8690
}
8791

8892
return nil, false
8993
}
9094

91-
func (b *backend[K, V]) pushBack(r *record[V], ttl time.Duration) {
92-
b.list.PushBack(r)
93-
r.initialized.Store(true)
95+
func (b *backend[K, V]) pushBack(elem *list.Element[record[V]], ttl time.Duration) {
96+
b.list.PushBack(elem)
97+
elem.Value.initialized.Store(true)
9498

9599
if n := b.overflow(); n > 0 {
96100
b.startGCOnce()
97101
b.timer.Reset(0)
98-
} else if r.deadline > 0 {
102+
} else if elem.Value.deadline > 0 {
99103
b.startGCOnce()
100-
if b.earliestExpireAt == 0 || r.deadline < b.earliestExpireAt {
101-
b.earliestExpireAt = r.deadline
104+
if b.earliestExpireAt == 0 || elem.Value.deadline < b.earliestExpireAt {
105+
b.earliestExpireAt = elem.Value.deadline
102106
b.timer.Reset(ttl)
103107
}
104108
}
@@ -124,29 +128,29 @@ func (b *backend[K, V]) runGC(now int64) {
124128
b.mu.Lock()
125129
defer b.mu.Unlock()
126130

127-
var overflowed map[*record[V]]bool
131+
var overflowed map[*list.Element[record[V]]]bool
128132

129133
if n := b.overflow(); n > 0 {
130-
overflowed = make(map[*record[V]]bool, n)
134+
overflowed = make(map[*list.Element[record[V]]]bool, n)
131135

132-
r := b.list.Front()
133-
overflowed[r] = true
136+
elem := b.list.Front()
137+
overflowed[elem] = true
134138

135139
for i := 1; i < n; i++ {
136-
r = r.Next()
137-
overflowed[r] = true
140+
elem = elem.Next()
141+
overflowed[elem] = true
138142
}
139143
}
140144

141145
var earliest int64
142146

143-
b.xmap.Range(func(key K, r *record[V]) bool {
144-
if r.initialized.Load() {
145-
if len(overflowed) > 0 && overflowed[r] || r.deadline > 0 && r.deadline < now {
147+
b.xmap.Range(func(key K, elem *list.Element[record[V]]) bool {
148+
if elem.Value.initialized.Load() {
149+
if len(overflowed) > 0 && overflowed[elem] || elem.Value.deadline > 0 && elem.Value.deadline < now {
146150
b.xmap.Delete(key)
147-
b.list.Remove(r)
148-
} else if r.deadline > 0 && (earliest == 0 || r.deadline < earliest) {
149-
earliest = r.deadline
151+
b.list.Remove(elem)
152+
} else if elem.Value.deadline > 0 && (earliest == 0 || elem.Value.deadline < earliest) {
153+
earliest = elem.Value.deadline
150154
}
151155
}
152156
return true

cache.go

+23-21
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"runtime"
55
"sync"
66
"time"
7+
8+
"github.com/mgnsk/list"
79
)
810

911
// Cache is an in-memory TTL cache with optional capacity.
@@ -32,17 +34,17 @@ func (c *Cache[K, V]) Exists(key K) bool {
3234
c.backend.mu.RLock()
3335
defer c.backend.mu.RUnlock()
3436

35-
r, ok := c.backend.xmap.Load(key)
36-
return ok && r.initialized.Load()
37+
elem, ok := c.backend.xmap.Load(key)
38+
return ok && elem.Value.initialized.Load()
3739
}
3840

3941
// Get returns the value stored in the cache for key.
4042
func (c *Cache[K, V]) Get(key K) (value V, exists bool) {
4143
c.backend.mu.RLock()
4244
defer c.backend.mu.RUnlock()
4345

44-
if r, ok := c.backend.xmap.Load(key); ok && r.initialized.Load() {
45-
return r.value, true
46+
if elem, ok := c.backend.xmap.Load(key); ok && elem.Value.initialized.Load() {
47+
return elem.Value.value, true
4648
}
4749

4850
var zero V
@@ -54,9 +56,9 @@ func (c *Cache[K, V]) Get(key K) (value V, exists bool) {
5456
//
5557
// Range is allowed to modify the cache.
5658
func (c *Cache[K, V]) Range(f func(key K, value V) bool) {
57-
c.backend.Range(func(key K, r *record[V]) bool {
58-
if r.initialized.Load() {
59-
return f(key, r.value)
59+
c.backend.Range(func(key K, elem *list.Element[record[V]]) bool {
60+
if elem.Value.initialized.Load() {
61+
return f(key, elem.Value.value)
6062
}
6163
return true
6264
})
@@ -75,8 +77,8 @@ func (c *Cache[K, V]) Evict(key K) (value V, ok bool) {
7577
c.backend.mu.Lock()
7678
defer c.backend.mu.Unlock()
7779

78-
if r, ok := c.backend.evict(key); ok {
79-
return r.value, true
80+
if elem, ok := c.backend.evict(key); ok {
81+
return elem.Value.value, true
8082
}
8183

8284
var zero V
@@ -117,26 +119,26 @@ func (c *Cache[K, V]) Fetch(key K, ttl time.Duration, f func() (V, error)) (valu
117119

118120
// TryFetch is like Fetch but allows the TTL to be returned alongside the value from callback.
119121
func (c *Cache[K, V]) TryFetch(key K, f func() (V, time.Duration, error)) (value V, err error) {
120-
new, ok := c.pool.Get().(*record[V])
122+
new, ok := c.pool.Get().(*list.Element[record[V]])
121123
if !ok {
122-
new = newRecord[V]()
124+
new = list.NewElement(record[V]{})
123125
}
124126

125-
new.wg.Add(1)
126-
defer new.wg.Done()
127+
new.Value.wg.Add(1)
128+
defer new.Value.wg.Done()
127129

128130
loadOrStore:
129-
if r, loaded := c.backend.LoadOrStore(key, new); loaded {
130-
if r.initialized.Load() {
131+
if elem, loaded := c.backend.LoadOrStore(key, new); loaded {
132+
if elem.Value.initialized.Load() {
131133
c.pool.Put(new)
132-
return r.value, nil
134+
return elem.Value.value, nil
133135
}
134136

135-
r.wg.Wait()
137+
elem.Value.wg.Wait()
136138

137-
if r.initialized.Load() {
139+
if elem.Value.initialized.Load() {
138140
c.pool.Put(new)
139-
return r.value, nil
141+
return elem.Value.value, nil
140142
}
141143

142144
goto loadOrStore
@@ -164,9 +166,9 @@ loadOrStore:
164166
return zero, err
165167
}
166168

167-
new.value = value
169+
new.Value.value = value
168170
if ttl > 0 {
169-
new.deadline = time.Now().Add(ttl).UnixNano()
171+
new.Value.deadline = time.Now().Add(ttl).UnixNano()
170172
}
171173

172174
c.backend.mu.Lock()

go.mod

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/mgnsk/evcache/v3
33
go 1.19
44

55
require (
6-
github.com/mgnsk/list v0.0.0-20230828152933-f6729dafd361
6+
github.com/mgnsk/list v0.0.0-20230904165030-d976a4f02c5c
77
github.com/onsi/gomega v1.27.10
88
)
99

go.sum

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
88
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
99
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
1010
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
11-
github.com/mgnsk/list v0.0.0-20230828152933-f6729dafd361 h1:JBd03hTO20OfizYtUXPTVnyM5NFGRqiJc6YABtnPcuE=
12-
github.com/mgnsk/list v0.0.0-20230828152933-f6729dafd361/go.mod h1:NSeUCVwmqTJGeKJW1DFe0En63T065/tLyj5HPwwBZd0=
11+
github.com/mgnsk/list v0.0.0-20230904165030-d976a4f02c5c h1:5aNAw0KUsCf4tO2SAoY06TZWkUoWN+Ap7WfoI+OAxbg=
12+
github.com/mgnsk/list v0.0.0-20230904165030-d976a4f02c5c/go.mod h1:NSeUCVwmqTJGeKJW1DFe0En63T065/tLyj5HPwwBZd0=
1313
github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU=
1414
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
1515
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=

record.go

-44
This file was deleted.

0 commit comments

Comments
 (0)