123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com>
- // All rights reserved.
- //
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- package cache
- import (
- "sync"
- "unsafe"
- )
- type lruNode struct {
- n *Node
- h *Handle
- ban bool
- next, prev *lruNode
- }
- func (n *lruNode) insert(at *lruNode) {
- x := at.next
- at.next = n
- n.prev = at
- n.next = x
- x.prev = n
- }
- func (n *lruNode) remove() {
- if n.prev != nil {
- n.prev.next = n.next
- n.next.prev = n.prev
- n.prev = nil
- n.next = nil
- } else {
- panic("BUG: removing removed node")
- }
- }
- type lru struct {
- mu sync.Mutex
- capacity int
- used int
- recent lruNode
- }
- func (r *lru) reset() {
- r.recent.next = &r.recent
- r.recent.prev = &r.recent
- r.used = 0
- }
- func (r *lru) Capacity() int {
- r.mu.Lock()
- defer r.mu.Unlock()
- return r.capacity
- }
- func (r *lru) SetCapacity(capacity int) {
- var evicted []*lruNode
- r.mu.Lock()
- r.capacity = capacity
- for r.used > r.capacity {
- rn := r.recent.prev
- if rn == nil {
- panic("BUG: invalid LRU used or capacity counter")
- }
- rn.remove()
- rn.n.CacheData = nil
- r.used -= rn.n.Size()
- evicted = append(evicted, rn)
- }
- r.mu.Unlock()
- for _, rn := range evicted {
- rn.h.Release()
- }
- }
- func (r *lru) Promote(n *Node) {
- var evicted []*lruNode
- r.mu.Lock()
- if n.CacheData == nil {
- if n.Size() <= r.capacity {
- rn := &lruNode{n: n, h: n.GetHandle()}
- rn.insert(&r.recent)
- n.CacheData = unsafe.Pointer(rn)
- r.used += n.Size()
- for r.used > r.capacity {
- rn := r.recent.prev
- if rn == nil {
- panic("BUG: invalid LRU used or capacity counter")
- }
- rn.remove()
- rn.n.CacheData = nil
- r.used -= rn.n.Size()
- evicted = append(evicted, rn)
- }
- }
- } else {
- rn := (*lruNode)(n.CacheData)
- if !rn.ban {
- rn.remove()
- rn.insert(&r.recent)
- }
- }
- r.mu.Unlock()
- for _, rn := range evicted {
- rn.h.Release()
- }
- }
- func (r *lru) Ban(n *Node) {
- r.mu.Lock()
- if n.CacheData == nil {
- n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true})
- } else {
- rn := (*lruNode)(n.CacheData)
- if !rn.ban {
- rn.remove()
- rn.ban = true
- r.used -= rn.n.Size()
- r.mu.Unlock()
- rn.h.Release()
- rn.h = nil
- return
- }
- }
- r.mu.Unlock()
- }
- func (r *lru) Evict(n *Node) {
- r.mu.Lock()
- rn := (*lruNode)(n.CacheData)
- if rn == nil || rn.ban {
- r.mu.Unlock()
- return
- }
- n.CacheData = nil
- r.mu.Unlock()
- rn.h.Release()
- }
- func (r *lru) EvictNS(ns uint64) {
- var evicted []*lruNode
- r.mu.Lock()
- for e := r.recent.prev; e != &r.recent; {
- rn := e
- e = e.prev
- if rn.n.NS() == ns {
- rn.remove()
- rn.n.CacheData = nil
- r.used -= rn.n.Size()
- evicted = append(evicted, rn)
- }
- }
- r.mu.Unlock()
- for _, rn := range evicted {
- rn.h.Release()
- }
- }
- func (r *lru) EvictAll() {
- r.mu.Lock()
- back := r.recent.prev
- for rn := back; rn != &r.recent; rn = rn.prev {
- rn.n.CacheData = nil
- }
- r.reset()
- r.mu.Unlock()
- for rn := back; rn != &r.recent; rn = rn.prev {
- rn.h.Release()
- }
- }
- func (r *lru) Close() error {
- return nil
- }
- // NewLRU create a new LRU-cache.
- func NewLRU(capacity int) Cacher {
- r := &lru{capacity: capacity}
- r.reset()
- return r
- }
|