Skip to content

Commit 7ed4369

Browse files
committed
Bring back OrderedRange as Do, but make it safer
1 parent 7873964 commit 7ed4369

File tree

2 files changed

+77
-19
lines changed

2 files changed

+77
-19
lines changed

cache.go

+46-19
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type Cache struct {
4040
wg sync.WaitGroup
4141
mu sync.RWMutex
4242
onceLoop sync.Once
43+
loopMu sync.Mutex
4344
lfuEnabled bool
4445
list *ringList
4546
afterEvict EvictionCallback
@@ -52,7 +53,7 @@ type Builder func(*Cache)
5253
// New creates an empty cache.
5354
//
5455
// Cache must be closed after usage has stopped
55-
// to prevent a leaking goroutine.
56+
// to prevent a leaking resources.
5657
//
5758
// It is not safe to close the cache while in use.
5859
func New() Builder {
@@ -228,15 +229,41 @@ func (c *Cache) Range(f func(key, value interface{}) bool) {
228229
})
229230
}
230231

231-
// Evict a key. After Evict returns, no Get or Fetch will load the key.
232-
func (c *Cache) Evict(key interface{}) {
233-
c.mu.RLock()
234-
defer c.mu.RUnlock()
235-
r, ok := c.records.LoadAndDelete(key)
236-
if !ok {
237-
return
232+
// Do calls f sequentially for each key and value present in the cache
233+
// in order. If f returns false, Do stops the iteration.
234+
// When LFU is used, the order is from least to most frequently used,
235+
// otherwise it is the insertion order with eldest first by default.
236+
//
237+
// It is not safe to use Do concurrently with any other method
238+
// except Exists and Get or a deadlock may occur. f is not allowed
239+
// to modify the cache.
240+
func (c *Cache) Do(f func(key, value interface{}) bool) {
241+
c.loopMu.Lock()
242+
defer c.loopMu.Unlock()
243+
c.list.Do(func(key interface{}) bool {
244+
r, ok := c.records.Load(key)
245+
if !ok {
246+
panic("evcache: Do used concurrently")
247+
}
248+
v, ok := r.(*record).Load()
249+
if !ok {
250+
panic("evcache: Do used concurrently")
251+
}
252+
return f(key, v)
253+
})
254+
}
255+
256+
// Pop evicts and returns the oldest key and value. If LFU ordering is
257+
// enabled, then the least frequently used key and value.
258+
func (c *Cache) Pop() (key, value interface{}) {
259+
for {
260+
if key = c.list.Pop(); key == nil {
261+
return nil, nil
262+
}
263+
if value, ok := c.LoadAndEvict(key); ok {
264+
return key, value
265+
}
238266
}
239-
c.finalizeAsync(key, r.(*record))
240267
}
241268

242269
// LoadAndEvict evicts a key and returns its value.
@@ -251,17 +278,15 @@ func (c *Cache) LoadAndEvict(key interface{}) (interface{}, bool) {
251278
return c.finalizeSync(key, r.(*record))
252279
}
253280

254-
// Pop evicts and returns the oldest key and value. If LFU ordering is
255-
// enabled, then the least frequently used key and value.
256-
func (c *Cache) Pop() (key, value interface{}) {
257-
for {
258-
if key = c.list.Pop(); key == nil {
259-
return nil, nil
260-
}
261-
if value, ok := c.LoadAndEvict(key); ok {
262-
return key, value
263-
}
281+
// Evict a key and its value from the cache.
282+
func (c *Cache) Evict(key interface{}) {
283+
c.mu.RLock()
284+
defer c.mu.RUnlock()
285+
r, ok := c.records.LoadAndDelete(key)
286+
if !ok {
287+
return
264288
}
289+
c.finalizeAsync(key, r.(*record))
265290
}
266291

267292
// Flush evicts all keys from the cache.
@@ -367,7 +392,9 @@ func (c *Cache) runLoop() {
367392
case <-c.stopLoop:
368393
return
369394
case now := <-ticker.C:
395+
c.loopMu.Lock()
370396
c.processRecords(now.UnixNano())
397+
c.loopMu.Unlock()
371398
}
372399
}
373400
}()

cache_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,37 @@ var _ = Describe("ranging over values", func() {
534534
})
535535
})
536536

537+
var _ = Describe("ordered iteration of records", func() {
538+
var (
539+
n = 10
540+
c *evcache.Cache
541+
)
542+
543+
BeforeEach(func() {
544+
c = evcache.New().
545+
WithCapacity(uint32(n)).
546+
Build()
547+
for i := 0; i < n; i++ {
548+
c.Set(i, i, 0)
549+
}
550+
Expect(c.Len()).To(Equal(n))
551+
})
552+
553+
Specify("Do is ordered", func() {
554+
var keys []int
555+
c.Do(func(key, value interface{}) bool {
556+
Expect(value).To(Equal(key))
557+
keys = append(keys, key.(int))
558+
return true
559+
})
560+
Expect(keys).To(HaveLen(n))
561+
Expect(sort.IntsAreSorted(keys)).To(BeTrue())
562+
563+
c.Close()
564+
Expect(c.Len()).To(BeZero())
565+
})
566+
})
567+
537568
var _ = Describe("overflow when setting values", func() {
538569
var (
539570
n = 100

0 commit comments

Comments
 (0)