-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcache.go
150 lines (135 loc) · 3.63 KB
/
cache.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package coalescent
import "sync"
type Cache struct {
mu sync.Mutex
tree Tree
}
func (c *Cache) Clone() *Cache {
if c == nil {
return nil
}
return &Cache{tree: c.tree.Clone()}
}
// Delete will unconditionally delete the entry under the given key, returning
// the entry if any was present.
func (c *Cache) Delete(k []byte) (interface{}, bool) {
c.mu.Lock()
v, ok := c.delete(k)
c.mu.Unlock()
return v, ok
}
// DeleteIf will delete the entry under the given key if it matches the passed
// predicate function, returning true if an entry was deleted. The predicate
// function must not panic, and should be as fast as possible.
func (c *Cache) DeleteIf(k []byte, pred func(interface{}) bool) bool {
c.mu.Lock()
ok := c.deleteIf(k, pred)
c.mu.Unlock()
return ok
}
// Fetch will find or create an entry under the given key. The passed init
// funcion should be simple, quick, and must not panic. If init is nil, a
// default function will be used. The second return value will be false if a
// new entry was created.
func (c *Cache) Fetch(k []byte, init func() interface{}) (interface{}, bool) {
if init == nil {
// FIXME: Is there something sensible we can do here?
panic("nil init function")
}
if v, ok := c.getOrLock(k); ok {
return v, true
}
v := init()
c.insert(k, v)
c.mu.Unlock()
return v, false
}
// Get will return the value, if any, under a given key.
func (c *Cache) Get(k []byte) (interface{}, bool) {
if tree := c.tree.Load(); tree != nil {
v, ok := tree.Get(k)
if ok {
return v, true
}
}
return nil, false
}
// Insert will unconditionally insert a new entry under the given key,
// returning the old entry if any was present.
func (c *Cache) Insert(k []byte, v interface{}) (interface{}, bool) {
c.mu.Lock()
v, ok := c.insert(k, v)
c.mu.Unlock()
return v, ok
}
type predicate func(interface{}, bool) bool
// delete must only be called while the lock is held.
func (c *Cache) delete(k []byte) (interface{}, bool) {
if tree := c.tree.Get(); tree != nil {
tree, old, ok := tree.Delete(k)
if ok {
c.tree.Store(tree)
}
return old, ok
}
return nil, false
}
// deleteIf must only be called while the lock is held.
func (c *Cache) deleteIf(k []byte, pred func(interface{}) bool) bool {
if pred == nil {
return false
}
if tree := c.tree.Get(); tree != nil {
tree, old, ok := tree.Delete(k)
if ok && pred(old) {
c.tree.Store(tree)
return true
}
}
return false
}
// getOrLock does a double-checked Get on the underlying tree, to avoid locking
// if the cache already has an entry for the provided key. It will either
// return the found value and true with the lock released, or nil and false
// with the lock held.
func (c *Cache) getOrLock(k []byte) (interface{}, bool) {
if tree := c.tree.Load(); tree != nil {
v, ok := tree.Get(k)
if ok {
return v, true
}
c.mu.Lock()
// If the tree hasn't changed, we don't need to recheck.
if tree == c.tree.Get() {
return nil, false
}
} else {
c.mu.Lock()
}
if tree := c.tree.Get(); tree != nil {
v, ok := tree.Get(k)
if ok {
c.mu.Unlock()
return v, true
}
}
return nil, false
}
// insert must only be called while the lock is held.
func (c *Cache) insert(k []byte, v interface{}) (interface{}, bool) {
tree, old, ok := c.tree.GetOrNew().Insert(k, v)
c.tree.Store(tree)
return old, ok
}
// insertOrReplaceIf must only be called while the lock is held.
// func (c *Cache) insertOrReplaceIf(k []byte, v interface{}, pred func(interface{}) bool) bool {
// if pred == nil {
// return false
// }
// tree, old, ok := c.tree.GetOrNew().Insert(k, v)
// if !ok || pred(old) {
// c.tree.Store(tree)
// return true
// }
// return false
// }