Skip to content

Commit 0d3b0d0

Browse files
committed
chore: refactor and implement new event handlers
1 parent b0fa077 commit 0d3b0d0

5 files changed

Lines changed: 110 additions & 65 deletions

File tree

arc/arc.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,14 @@ func (a *arc) RegisterOnExpired(f func(key, value interface{})) {
179179
a.t2.RegisterOnExpired(f)
180180
}
181181

182-
func (a *arc) Notify(fn func(libcache.Event), ops ...libcache.Op) {
183-
a.t1.Notify(fn, ops...)
184-
a.t2.Notify(fn, ops...)
182+
func (a *arc) Notify(ch chan<- libcache.Event, ops ...libcache.Op) {
183+
a.t1.Notify(ch, ops...)
184+
a.t2.Notify(ch, ops...)
185+
}
186+
187+
func (a *arc) Ignore(ch chan<- libcache.Event, ops ...libcache.Op) {
188+
a.t1.Ignore(ch, ops...)
189+
a.t2.Ignore(ch, ops...)
185190
}
186191

187192
func (a *arc) GC() time.Duration {

cache.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,14 @@ type Cache interface {
6363
//
6464
// Deprecated: use Notify instead.
6565
RegisterOnExpired(f func(key, value interface{}))
66-
// Notify causes cahce to relay events to fn.
67-
// If no operations are provided, all incoming operations will be relayed to fn.
66+
// Notify causes cache to relay events to ch.
67+
// If no operations are provided, all incoming operations will be relayed to ch.
6868
// Otherwise, just the provided operations will.
69-
Notify(fn func(Event), ops ...Op)
69+
Notify(ch chan<- Event, ops ...Op)
70+
// Ignore causes the provided operations to be ignored. Ignore undoes the effect
71+
// of any prior calls to Notify for the provided operations.
72+
// If no operations are provided, ch removed.
73+
Ignore(ch chan<- Event, ops ...Op)
7074
// GC runs a garbage collection and blocks the caller until the
7175
// all expired items from the cache evicted.
7276
//
@@ -101,8 +105,8 @@ func (c *cache) Peek(key interface{}) (interface{}, bool) {
101105

102106
func (c *cache) Update(key interface{}, value interface{}) {
103107
c.mu.Lock()
104-
defer c.mu.Unlock()
105108
c.unsafe.Update(key, value)
109+
c.mu.Unlock()
106110
}
107111

108112
func (c *cache) Store(key interface{}, value interface{}) {
@@ -189,9 +193,15 @@ func (c *cache) RegisterOnExpired(f func(key, value interface{})) {
189193
c.mu.Unlock()
190194
}
191195

192-
func (c *cache) Notify(fn func(Event), ops ...Op) {
196+
func (c *cache) Notify(ch chan<- Event, ops ...Op) {
197+
c.mu.Lock()
198+
c.unsafe.Notify(ch, ops...)
199+
c.mu.Unlock()
200+
}
201+
202+
func (c *cache) Ignore(ch chan<- Event, ops ...Op) {
193203
c.mu.Lock()
194-
c.unsafe.Notify(fn, ops...)
204+
c.unsafe.Ignore(ch, ops...)
195205
c.mu.Unlock()
196206
}
197207

cache_test.go

Lines changed: 19 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,15 @@ func TestOnEvicted(t *testing.T) {
213213
for _, tt := range cacheTests {
214214
t.Run("Test"+tt.cont.String()+"CacheOnEvicted", func(t *testing.T) {
215215
cache := tt.cont.New(20)
216-
send := make(chan interface{})
216+
send := make(chan libcache.Event, 10)
217217
done := make(chan bool)
218218
evictedKeys := make([]interface{}, 0, 2)
219-
cache.RegisterOnEvicted(func(key, value interface{}) {
220-
send <- key
221-
})
219+
cache.Notify(send, libcache.Remove)
222220

223221
go func() {
224222
for {
225-
key := <-send
226-
evictedKeys = append(evictedKeys, key)
223+
e := <-send
224+
evictedKeys = append(evictedKeys, e.Key)
227225
if len(evictedKeys) >= 2 {
228226
done <- true
229227
return
@@ -246,27 +244,6 @@ func TestOnEvicted(t *testing.T) {
246244
}
247245
}
248246

249-
func TestOnExpired(t *testing.T) {
250-
for _, tt := range cacheTests {
251-
t.Run("Test"+tt.cont.String()+"CacheOnExpired", func(t *testing.T) {
252-
expiredKeys := make([]interface{}, 0, 2)
253-
cache := tt.cont.New(0)
254-
cache.RegisterOnExpired(func(key, _ interface{}) {
255-
expiredKeys = append(expiredKeys, key)
256-
})
257-
cache.SetTTL(time.Millisecond)
258-
259-
cache.Store(1, 1234)
260-
cache.Store(2, 1234)
261-
262-
time.Sleep(time.Millisecond * 2)
263-
cache.Peek(1)
264-
265-
assert.ElementsMatch(t, []interface{}{1, 2}, expiredKeys)
266-
})
267-
}
268-
}
269-
270247
func TestExpiring(t *testing.T) {
271248
for _, tt := range cacheTests {
272249
t.Run("Test"+tt.cont.String()+"CacheExpiring", func(t *testing.T) {
@@ -313,24 +290,33 @@ func TestNotify(t *testing.T) {
313290
for _, tt := range cacheTests {
314291
t.Run("Test"+tt.cont.String()+"CacheNotify", func(t *testing.T) {
315292
got := 0
293+
c := make(chan libcache.Event, 10)
316294
cache := tt.cont.New(0)
317-
fn := func(e libcache.Event) {
318-
t.Logf("Operation %s on Key %v \n", e.Op, e.Key)
319-
got += e.Key.(int)
320-
}
321295

322-
cache.Notify(fn, libcache.Read, libcache.Write, libcache.Remove)
296+
cache.Notify(c)
323297

324298
cache.Load(1)
325299
cache.StoreWithTTL(1, 0, time.Second)
326300
cache.Peek(1)
327301
cache.Delete(1)
302+
close(c)
303+
304+
for e := range c {
305+
t.Logf("Operation %s on Key %v \n", e.Op, e.Key)
306+
got += e.Key.(int)
307+
}
328308

329309
if tt.cont == libcache.ARC {
330310
assert.Equal(t, 7, got)
331311
} else {
332312
assert.Equal(t, 4, got)
333313
}
314+
315+
// check it will not try to write on chan after ignore
316+
cache.Ignore(c)
317+
for i := 0; i < 10; i++ {
318+
cache.Store(i, i)
319+
}
334320
})
335321
}
336322
}
@@ -351,6 +337,7 @@ func TestGC(t *testing.T) {
351337
})
352338
}
353339
}
340+
354341
func BenchmarkCache(b *testing.B) {
355342
for _, tt := range cacheTests {
356343
b.Run("Benchmark"+tt.cont.String()+"Cache", func(b *testing.B) {

idle/idle.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,5 @@ func (idle) Purge() {}
3636
func (idle) SetTTL(ttl time.Duration) {}
3737
func (idle) RegisterOnExpired(f func(key, value interface{})) {}
3838
func (idle) RegisterOnEvicted(f func(key, value interface{})) {}
39-
func (idle) Notify(fn func(libcache.Event), op ...libcache.Op) {}
39+
func (idle) Notify(ch chan<- libcache.Event, ops ...libcache.Op) {}
40+
func (idle) Ignore(ch chan<- libcache.Event, ops ...libcache.Op) {}

internal/cache.go

Lines changed: 65 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,15 @@ package internal
33
import (
44
"container/heap"
55
"fmt"
6-
"strconv"
76
"time"
87
)
98

109
// Op describes a set of cache operations.
11-
type Op int
10+
type Op uint8
1211

1312
// These are the generalized cache operations that can trigger a event.
1413
const (
15-
Read Op = iota
14+
Read Op = iota + 1
1615
Write
1716
Remove
1817
maxOp
@@ -31,6 +30,22 @@ func (op Op) String() string {
3130
}
3231
}
3332

33+
type handler struct {
34+
mask [((maxOp - 1) + 7) / 8]uint8
35+
}
36+
37+
func (h *handler) want(op Op) bool {
38+
return (h.mask[op/8]>>uint8(op&7))&1 != 0
39+
}
40+
41+
func (h *handler) set(op Op) {
42+
h.mask[op/8] |= 1 << uint8(op&7)
43+
}
44+
45+
func (h *handler) clear(op Op) {
46+
h.mask[op/8] &^= 1 << uint8(op&7)
47+
}
48+
3449
// Collection represents the cache underlying data structure,
3550
// and defines the functions or operations that can be applied to the data elements.
3651
type Collection interface {
@@ -77,7 +92,7 @@ type Cache struct {
7792
coll Collection
7893
heap expiringHeap
7994
entries map[interface{}]*Entry
80-
events [maxOp][]func(Event)
95+
handlers map[chan<- Event]*handler
8196
ttl time.Duration
8297
capacity int
8398
}
@@ -165,7 +180,7 @@ func (c *Cache) Update(key, value interface{}) {
165180
func (c *Cache) Purge() {
166181
defer c.coll.Init()
167182

168-
if len(c.events[Remove]) == 0 {
183+
if len(c.handlers) == 0 {
169184
c.entries = make(map[interface{}]*Entry)
170185
c.heap = nil
171186
return
@@ -260,8 +275,14 @@ func (c *Cache) emit(op Op, k, v interface{}, exp time.Time, ok bool) {
260275
Ok: ok,
261276
}
262277

263-
for _, fn := range c.events[op] {
264-
fn(e)
278+
for c, h := range c.handlers {
279+
if h.want(op) {
280+
// send but do not block for it
281+
select {
282+
case c <- e:
283+
default:
284+
}
285+
}
265286
}
266287
}
267288

@@ -304,38 +325,58 @@ func (c *Cache) Cap() int {
304325
return c.capacity
305326
}
306327

307-
// Notify causes cahce to relay events to fn.
308-
// If no operations are provided, all incoming operations will be relayed to fn.
328+
// Notify causes cache to relay events to ch.
329+
// If no operations are provided, all incoming operations will be relayed to ch.
309330
// Otherwise, just the provided operations will.
310-
func (c *Cache) Notify(fn func(Event), ops ...Op) {
331+
func (c *Cache) Notify(ch chan<- Event, ops ...Op) {
332+
if ch == nil {
333+
panic("libcache: Notify using nil channel")
334+
}
335+
336+
h := new(handler)
337+
c.handlers[ch] = h
338+
339+
if len(ops) == 0 {
340+
for i := 1; i <= int(maxOp); i++ {
341+
h.set(Op(i))
342+
}
343+
return
344+
}
345+
346+
for _, op := range ops {
347+
h.set(op)
348+
}
349+
}
350+
351+
// Ignore causes the provided ops to be ignored. Ignore undoes the effect
352+
// of any prior calls to Notify for the provided ops.
353+
// If no ops are provided, ch removed.
354+
func (c *Cache) Ignore(ch chan<- Event, ops ...Op) {
311355
if len(ops) == 0 {
312-
ops = append(ops, Read, Write, Remove)
356+
delete(c.handlers, ch)
357+
return
358+
}
359+
360+
h, ok := c.handlers[ch]
361+
if !ok {
362+
return
313363
}
314364

315365
for _, op := range ops {
316-
if op < 0 && op >= maxOp {
317-
panic("libcache: notify on unknown operation #" + strconv.Itoa(int(op)))
318-
}
319-
c.events[op] = append(c.events[op], fn)
366+
h.clear(op)
320367
}
321368
}
322369

323370
// RegisterOnEvicted registers a function,
324371
// to call it when an entry is purged from the cache.
325372
func (c *Cache) RegisterOnEvicted(fn func(key, value interface{})) {
326-
c.Notify(func(e Event) {
327-
fn(e.Key, e.Value)
328-
}, Remove)
373+
panic("RegisterOnEvicted no longer available")
329374
}
330375

331376
// RegisterOnExpired registers a function,
332377
// to call it when an entry TTL elapsed.
333378
func (c *Cache) RegisterOnExpired(fn func(key, value interface{})) {
334-
c.Notify(func(e Event) {
335-
if e.Expiry.Before(time.Now()) {
336-
fn(e.Key, e.Value)
337-
}
338-
}, Remove)
379+
panic("RegisterOnExpired no longer available")
339380
}
340381

341382
// New return new abstracted cache.
@@ -344,6 +385,7 @@ func New(c Collection, cap int) *Cache {
344385
coll: c,
345386
capacity: cap,
346387
entries: make(map[interface{}]*Entry),
388+
handlers: make(map[chan<- Event]*handler),
347389
}
348390
}
349391

0 commit comments

Comments
 (0)