safe_lru.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. package lru
  2. import (
  3. "sync"
  4. )
  5. const (
  6. // DefaultEvictedBufferSize defines the default buffer size to store evicted key/val
  7. DefaultEvictedBufferSize = 16
  8. )
  9. // SafeLRU is a thread-safe fixed size LRU cache.
  10. type SafeLRU struct {
  11. lru LRU
  12. evictedKeys, evictedValues []any
  13. onEvictedCB func(k, v any)
  14. lock sync.RWMutex
  15. }
  16. // NewSafe creates an LRU of the given size.
  17. func NewSafe(size int) LRU {
  18. return NewWithEvict(size, nil)
  19. }
  20. // NewWithEvict constructs a fixed size cache with the given eviction
  21. // callback.
  22. func NewWithEvict(size int, onEvicted func(key, value any)) LRU {
  23. // create a cache with default settings
  24. c := &SafeLRU{
  25. onEvictedCB: onEvicted,
  26. }
  27. if onEvicted != nil {
  28. c.initEvictBuffers()
  29. onEvicted = c.onEvicted
  30. }
  31. c.lru = NewSimple(size, onEvicted)
  32. return c
  33. }
  34. func (c *SafeLRU) initEvictBuffers() {
  35. c.evictedKeys = make([]any, 0, DefaultEvictedBufferSize)
  36. c.evictedValues = make([]any, 0, DefaultEvictedBufferSize)
  37. }
  38. // onEvicted save evicted key/val and sent in externally registered callback
  39. // outside of critical section
  40. func (c *SafeLRU) onEvicted(k, v any) {
  41. c.evictedKeys = append(c.evictedKeys, k)
  42. c.evictedValues = append(c.evictedValues, v)
  43. }
  44. // Purge is used to completely clear the cache.
  45. func (c *SafeLRU) Purge() {
  46. var ks, vs []any
  47. c.lock.Lock()
  48. c.lru.Purge()
  49. if c.onEvictedCB != nil && len(c.evictedKeys) > 0 {
  50. ks, vs = c.evictedKeys, c.evictedValues
  51. c.initEvictBuffers()
  52. }
  53. c.lock.Unlock()
  54. // invoke callback outside of critical section
  55. if c.onEvictedCB != nil {
  56. for i := 0; i < len(ks); i++ {
  57. c.onEvictedCB(ks[i], vs[i])
  58. }
  59. }
  60. }
  61. // Add adds a value to the cache. Returns true if an eviction occurred.
  62. func (c *SafeLRU) Add(key, value any) (evicted bool) {
  63. var k, v any
  64. c.lock.Lock()
  65. evicted = c.lru.Add(key, value)
  66. if c.onEvictedCB != nil && evicted {
  67. k, v = c.evictedKeys[0], c.evictedValues[0]
  68. c.evictedKeys, c.evictedValues = c.evictedKeys[:0], c.evictedValues[:0]
  69. }
  70. c.lock.Unlock()
  71. if c.onEvictedCB != nil && evicted {
  72. c.onEvictedCB(k, v)
  73. }
  74. return
  75. }
  76. // Get looks up a key's value from the cache.
  77. func (c *SafeLRU) Get(key any) (value any, ok bool) {
  78. c.lock.Lock()
  79. value, ok = c.lru.Get(key)
  80. c.lock.Unlock()
  81. return value, ok
  82. }
  83. // Contains checks if a key is in the cache, without updating the
  84. // recent-ness or deleting it for being stale.
  85. func (c *SafeLRU) Contains(key any) bool {
  86. c.lock.RLock()
  87. containKey := c.lru.Contains(key)
  88. c.lock.RUnlock()
  89. return containKey
  90. }
  91. // Peek returns the key value (or undefined if not found) without updating
  92. // the "recently used"-ness of the key.
  93. func (c *SafeLRU) Peek(key any) (value any, ok bool) {
  94. c.lock.RLock()
  95. value, ok = c.lru.Peek(key)
  96. c.lock.RUnlock()
  97. return value, ok
  98. }
  99. // ContainsOrAdd checks if a key is in the cache without updating the
  100. // recent-ness or deleting it for being stale, and if not, adds the value.
  101. // Returns whether found and whether an eviction occurred.
  102. func (c *SafeLRU) ContainsOrAdd(key, value any) (ok, evicted bool) {
  103. var k, v any
  104. c.lock.Lock()
  105. if c.lru.Contains(key) {
  106. c.lock.Unlock()
  107. return true, false
  108. }
  109. evicted = c.lru.Add(key, value)
  110. if c.onEvictedCB != nil && evicted {
  111. k, v = c.evictedKeys[0], c.evictedValues[0]
  112. c.evictedKeys, c.evictedValues = c.evictedKeys[:0], c.evictedValues[:0]
  113. }
  114. c.lock.Unlock()
  115. if c.onEvictedCB != nil && evicted {
  116. c.onEvictedCB(k, v)
  117. }
  118. return false, evicted
  119. }
  120. // PeekOrAdd checks if a key is in the cache without updating the
  121. // recent-ness or deleting it for being stale, and if not, adds the value.
  122. // Returns whether found and whether an eviction occurred.
  123. func (c *SafeLRU) PeekOrAdd(key, value any) (previous any, ok, evicted bool) {
  124. var k, v any
  125. c.lock.Lock()
  126. previous, ok = c.lru.Peek(key)
  127. if ok {
  128. c.lock.Unlock()
  129. return previous, true, false
  130. }
  131. evicted = c.lru.Add(key, value)
  132. if c.onEvictedCB != nil && evicted {
  133. k, v = c.evictedKeys[0], c.evictedValues[0]
  134. c.evictedKeys, c.evictedValues = c.evictedKeys[:0], c.evictedValues[:0]
  135. }
  136. c.lock.Unlock()
  137. if c.onEvictedCB != nil && evicted {
  138. c.onEvictedCB(k, v)
  139. }
  140. return nil, false, evicted
  141. }
  142. // Remove removes the provided key from the cache.
  143. func (c *SafeLRU) Remove(key any) (present bool) {
  144. var k, v any
  145. c.lock.Lock()
  146. present = c.lru.Remove(key)
  147. if c.onEvictedCB != nil && present {
  148. k, v = c.evictedKeys[0], c.evictedValues[0]
  149. c.evictedKeys, c.evictedValues = c.evictedKeys[:0], c.evictedValues[:0]
  150. }
  151. c.lock.Unlock()
  152. if c.onEvictedCB != nil && present {
  153. c.onEvicted(k, v)
  154. }
  155. return
  156. }
  157. // Resize changes the cache size.
  158. func (c *SafeLRU) Resize(size int) (evicted int) {
  159. var ks, vs []any
  160. c.lock.Lock()
  161. evicted = c.lru.Resize(size)
  162. if c.onEvictedCB != nil && evicted > 0 {
  163. ks, vs = c.evictedKeys, c.evictedValues
  164. c.initEvictBuffers()
  165. }
  166. c.lock.Unlock()
  167. if c.onEvictedCB != nil && evicted > 0 {
  168. for i := 0; i < len(ks); i++ {
  169. c.onEvictedCB(ks[i], vs[i])
  170. }
  171. }
  172. return evicted
  173. }
  174. // RemoveOldest removes the oldest item from the cache.
  175. func (c *SafeLRU) RemoveOldest() (key, value any, ok bool) {
  176. var k, v any
  177. c.lock.Lock()
  178. key, value, ok = c.lru.RemoveOldest()
  179. if c.onEvictedCB != nil && ok {
  180. k, v = c.evictedKeys[0], c.evictedValues[0]
  181. c.evictedKeys, c.evictedValues = c.evictedKeys[:0], c.evictedValues[:0]
  182. }
  183. c.lock.Unlock()
  184. if c.onEvictedCB != nil && ok {
  185. c.onEvictedCB(k, v)
  186. }
  187. return
  188. }
  189. // GetOldest returns the oldest entry
  190. func (c *SafeLRU) GetOldest() (key, value any, ok bool) {
  191. c.lock.RLock()
  192. key, value, ok = c.lru.GetOldest()
  193. c.lock.RUnlock()
  194. return
  195. }
  196. // Keys returns a slice of the keys in the cache, from oldest to newest.
  197. func (c *SafeLRU) Keys() []any {
  198. c.lock.RLock()
  199. keys := c.lru.Keys()
  200. c.lock.RUnlock()
  201. return keys
  202. }
  203. // Values returns a slice of the values in the cache, from oldest to newest.
  204. func (c *SafeLRU) Values() []any {
  205. c.lock.RLock()
  206. keys := c.lru.Values()
  207. c.lock.RUnlock()
  208. return keys
  209. }
  210. // Len returns the number of items in the cache.
  211. func (c *SafeLRU) Len() int {
  212. c.lock.RLock()
  213. length := c.lru.Len()
  214. c.lock.RUnlock()
  215. return length
  216. }