github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/syndtr/goleveldb/leveldb/cache/lru.go (about) 1 // Copyright (c) 2012, Suryandaru Triandana <syndtr@gmail.com> 2 // All rights reserved. 3 // 4 // Use of this source code is governed by a BSD-style license that can be 5 // found in the LICENSE file. 6 7 package cache 8 9 import ( 10 "sync" 11 "unsafe" 12 ) 13 14 type lruNode struct { 15 n *Node 16 h *Handle 17 ban bool 18 19 next, prev *lruNode 20 } 21 22 func (n *lruNode) insert(at *lruNode) { 23 x := at.next 24 at.next = n 25 n.prev = at 26 n.next = x 27 x.prev = n 28 } 29 30 func (n *lruNode) remove() { 31 if n.prev != nil { 32 n.prev.next = n.next 33 n.next.prev = n.prev 34 n.prev = nil 35 n.next = nil 36 } else { 37 panic("BUG: removing removed node") 38 } 39 } 40 41 type lru struct { 42 mu sync.Mutex 43 capacity int 44 used int 45 recent lruNode 46 } 47 48 func (r *lru) reset() { 49 r.recent.next = &r.recent 50 r.recent.prev = &r.recent 51 r.used = 0 52 } 53 54 func (r *lru) Capacity() int { 55 r.mu.Lock() 56 defer r.mu.Unlock() 57 return r.capacity 58 } 59 60 func (r *lru) SetCapacity(capacity int) { 61 var evicted []*lruNode 62 63 r.mu.Lock() 64 r.capacity = capacity 65 for r.used > r.capacity { 66 rn := r.recent.prev 67 if rn == nil { 68 panic("BUG: invalid LRU used or capacity counter") 69 } 70 rn.remove() 71 rn.n.CacheData = nil 72 r.used -= rn.n.Size() 73 evicted = append(evicted, rn) 74 } 75 r.mu.Unlock() 76 77 for _, rn := range evicted { 78 rn.h.Release() 79 } 80 } 81 82 func (r *lru) Promote(n *Node) { 83 var evicted []*lruNode 84 85 r.mu.Lock() 86 if n.CacheData == nil { 87 if n.Size() <= r.capacity { 88 rn := &lruNode{n: n, h: n.GetHandle()} 89 rn.insert(&r.recent) 90 n.CacheData = unsafe.Pointer(rn) 91 r.used += n.Size() 92 93 for r.used > r.capacity { 94 rn := r.recent.prev 95 if rn == nil { 96 panic("BUG: invalid LRU used or capacity counter") 97 } 98 rn.remove() 99 rn.n.CacheData = nil 100 r.used -= rn.n.Size() 101 evicted = append(evicted, rn) 102 } 103 } 104 } else { 105 rn := (*lruNode)(n.CacheData) 106 if !rn.ban { 107 rn.remove() 108 rn.insert(&r.recent) 109 } 110 } 111 r.mu.Unlock() 112 113 for _, rn := range evicted { 114 rn.h.Release() 115 } 116 } 117 118 func (r *lru) Ban(n *Node) { 119 r.mu.Lock() 120 if n.CacheData == nil { 121 n.CacheData = unsafe.Pointer(&lruNode{n: n, ban: true}) 122 } else { 123 rn := (*lruNode)(n.CacheData) 124 if !rn.ban { 125 rn.remove() 126 rn.ban = true 127 r.used -= rn.n.Size() 128 r.mu.Unlock() 129 130 rn.h.Release() 131 rn.h = nil 132 return 133 } 134 } 135 r.mu.Unlock() 136 } 137 138 func (r *lru) Evict(n *Node) { 139 r.mu.Lock() 140 rn := (*lruNode)(n.CacheData) 141 if rn == nil || rn.ban { 142 r.mu.Unlock() 143 return 144 } 145 n.CacheData = nil 146 r.mu.Unlock() 147 148 rn.h.Release() 149 } 150 151 func (r *lru) EvictNS(ns uint64) { 152 var evicted []*lruNode 153 154 r.mu.Lock() 155 for e := r.recent.prev; e != &r.recent; { 156 rn := e 157 e = e.prev 158 if rn.n.NS() == ns { 159 rn.remove() 160 rn.n.CacheData = nil 161 r.used -= rn.n.Size() 162 evicted = append(evicted, rn) 163 } 164 } 165 r.mu.Unlock() 166 167 for _, rn := range evicted { 168 rn.h.Release() 169 } 170 } 171 172 func (r *lru) EvictAll() { 173 r.mu.Lock() 174 back := r.recent.prev 175 for rn := back; rn != &r.recent; rn = rn.prev { 176 rn.n.CacheData = nil 177 } 178 r.reset() 179 r.mu.Unlock() 180 181 for rn := back; rn != &r.recent; rn = rn.prev { 182 rn.h.Release() 183 } 184 } 185 186 func (r *lru) Close() error { 187 return nil 188 } 189 190 // NewLRU create a new LRU-cache. 191 func NewLRU(capacity int) Cacher { 192 r := &lru{capacity: capacity} 193 r.reset() 194 return r 195 }