Skip to content

Commit 5fb8262

Browse files
committed
Require only single threaded map, move locking to backend
1 parent 45f5a77 commit 5fb8262

File tree

3 files changed

+73
-40
lines changed

3 files changed

+73
-40
lines changed

backend.go

+46-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type recordList[V any] struct {
1414
type backend[K comparable, V any] struct {
1515
timer *time.Timer
1616
done chan struct{}
17-
xmap builtinMap[K, *record[V]]
17+
xmap backendMap[K, *record[V]]
1818
list recordList[V]
1919
earliestExpireAt int64
2020
cap int
@@ -39,9 +39,48 @@ func (b *backend[K, V]) close() error {
3939
return nil
4040
}
4141

42+
func (b *backend[K, V]) LoadOrStore(key K, new *record[V]) (old *record[V], loaded bool) {
43+
b.mu.RLock()
44+
if r, ok := b.xmap.Load(key); ok {
45+
b.mu.RUnlock()
46+
return r, true
47+
}
48+
b.mu.RUnlock()
49+
50+
b.mu.Lock()
51+
defer b.mu.Unlock()
52+
53+
if r, ok := b.xmap.Load(key); ok {
54+
return r, true
55+
}
56+
57+
b.xmap.Store(key, new)
58+
59+
return new, false
60+
}
61+
62+
func (b *backend[K, V]) Range(f func(key K, r *record[V]) bool) {
63+
b.mu.RLock()
64+
keys := make([]K, 0, b.xmap.Len())
65+
b.xmap.Range(func(key K, _ *record[V]) bool {
66+
keys = append(keys, key)
67+
return true
68+
})
69+
b.mu.RUnlock()
70+
71+
for _, key := range keys {
72+
b.mu.RLock()
73+
r, ok := b.xmap.Load(key)
74+
b.mu.RUnlock()
75+
if ok && !f(key, r) {
76+
return
77+
}
78+
}
79+
}
80+
4281
func (b *backend[K, V]) evict(key K) (*record[V], bool) {
43-
if r, ok := b.xmap[key]; ok && r.initialized.Load() {
44-
delete(b.xmap, key)
82+
if r, ok := b.xmap.Load(key); ok && r.initialized.Load() {
83+
b.xmap.Delete(key)
4584
b.list.Remove(r)
4685
return r, true
4786
}
@@ -101,16 +140,17 @@ func (b *backend[K, V]) runGC(now int64) {
101140

102141
var earliest int64
103142

104-
for key, r := range b.xmap {
143+
b.xmap.Range(func(key K, r *record[V]) bool {
105144
if r.initialized.Load() {
106145
if len(overflowed) > 0 && overflowed[r] || r.deadline > 0 && r.deadline < now {
107-
delete(b.xmap, key)
146+
b.xmap.Delete(key)
108147
b.list.Remove(r)
109148
} else if r.deadline > 0 && (earliest == 0 || r.deadline < earliest) {
110149
earliest = r.deadline
111150
}
112151
}
113-
}
152+
return true
153+
})
114154

115155
b.earliestExpireAt = earliest
116156
if earliest > 0 {

cache.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ func (c *Cache[K, V]) Exists(key K) bool {
3232
c.backend.mu.RLock()
3333
defer c.backend.mu.RUnlock()
3434

35-
r, ok := c.backend.xmap[key]
35+
r, ok := c.backend.xmap.Load(key)
3636
return ok && r.initialized.Load()
3737
}
3838

@@ -41,7 +41,7 @@ func (c *Cache[K, V]) Get(key K) (value V, exists bool) {
4141
c.backend.mu.RLock()
4242
defer c.backend.mu.RUnlock()
4343

44-
if r, ok := c.backend.xmap[key]; ok && r.initialized.Load() {
44+
if r, ok := c.backend.xmap.Load(key); ok && r.initialized.Load() {
4545
return r.value, true
4646
}
4747

@@ -54,7 +54,7 @@ func (c *Cache[K, V]) Get(key K) (value V, exists bool) {
5454
//
5555
// Range is allowed to modify the cache.
5656
func (c *Cache[K, V]) Range(f func(key K, value V) bool) {
57-
c.backend.xmap.Range(&c.backend.mu, func(key K, r *record[V]) bool {
57+
c.backend.Range(func(key K, r *record[V]) bool {
5858
if r.initialized.Load() {
5959
return f(key, r.value)
6060
}
@@ -126,7 +126,7 @@ func (c *Cache[K, V]) TryFetch(key K, f func() (V, time.Duration, error)) (value
126126
defer new.wg.Done()
127127

128128
loadOrStore:
129-
if r, loaded := c.backend.xmap.LoadOrStore(&c.backend.mu, key, new); loaded {
129+
if r, loaded := c.backend.LoadOrStore(key, new); loaded {
130130
if r.initialized.Load() {
131131
c.pool.Put(new)
132132
return r.value, nil
@@ -147,7 +147,7 @@ loadOrStore:
147147
c.backend.mu.Lock()
148148
defer c.backend.mu.Unlock()
149149

150-
delete(c.backend.xmap, key)
150+
c.backend.xmap.Delete(key)
151151

152152
panic(r)
153153
}
@@ -158,7 +158,7 @@ loadOrStore:
158158
c.backend.mu.Lock()
159159
defer c.backend.mu.Unlock()
160160

161-
delete(c.backend.xmap, key)
161+
c.backend.xmap.Delete(key)
162162

163163
var zero V
164164
return zero, err

map.go

+21-28
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,35 @@
11
package evcache
22

3-
import "sync"
3+
type backendMap[K comparable, V any] interface {
4+
Load(K) (V, bool)
5+
Store(K, V)
6+
Delete(K)
7+
Len() int
8+
Range(func(K, V) bool)
9+
}
410

511
type builtinMap[K comparable, V any] map[K]V
612

7-
func (m builtinMap[K, V]) LoadOrStore(mu *sync.RWMutex, key K, value V) (actual V, loaded bool) {
8-
mu.RLock()
9-
if v, ok := m[key]; ok {
10-
mu.RUnlock()
11-
return v, true
12-
}
13-
mu.RUnlock()
14-
15-
mu.Lock()
16-
defer mu.Unlock()
17-
18-
if v, ok := m[key]; ok {
19-
return v, true
20-
}
13+
func (m builtinMap[K, V]) Load(key K) (value V, ok bool) {
14+
value, ok = m[key]
15+
return
16+
}
2117

18+
func (m builtinMap[K, V]) Store(key K, value V) {
2219
m[key] = value
20+
}
2321

24-
return value, false
22+
func (m builtinMap[K, V]) Delete(key K) {
23+
delete(m, key)
2524
}
2625

27-
func (m builtinMap[K, V]) Range(mu *sync.RWMutex, f func(key K, value V) bool) {
28-
mu.RLock()
29-
keys := make([]K, 0, len(m))
30-
for key := range m {
31-
keys = append(keys, key)
32-
}
33-
mu.RUnlock()
26+
func (m builtinMap[K, V]) Len() int {
27+
return len(m)
28+
}
3429

35-
for _, key := range keys {
36-
mu.RLock()
37-
v, ok := m[key]
38-
mu.RUnlock()
39-
if ok && !f(key, v) {
30+
func (m builtinMap[K, V]) Range(f func(key K, value V) bool) {
31+
for k, v := range m {
32+
if !f(k, v) {
4033
return
4134
}
4235
}

0 commit comments

Comments
 (0)