123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- package cache
- import (
- "container/list"
- "sync"
- "sync/atomic"
- )
- const (
- // Number of cache data store will be 2 ^ concurrencyLevel.
- concurrencyLevel = 2
- segmentCount = 1 << concurrencyLevel
- segmentMask = segmentCount - 1
- )
- // entry stores cached entry key and value.
- type entry struct {
- // Structs with first field align to 64 bits will also be aligned to 64.
- // https://golang.org/pkg/sync/atomic/#pkg-note-BUG
- // hash is the hash value of this entry key
- hash uint64
- // accessTime is the last time this entry was accessed.
- accessTime int64 // Access atomically - must be aligned on 32-bit
- // writeTime is the last time this entry was updated.
- writeTime int64 // Access atomically - must be aligned on 32-bit
- // FIXME: More efficient way to store boolean flags
- invalidated int32
- loading int32
- key Key
- value atomic.Value // Store value
- // These properties are managed by only cache policy so do not need atomic access.
- // accessList is the list (ordered by access time) this entry is currently in.
- accessList *list.Element
- // writeList is the list (ordered by write time) this entry is currently in.
- writeList *list.Element
- // listID is ID of the list which this entry is currently in.
- listID uint8
- }
- func newEntry(k Key, v Value, h uint64) *entry {
- en := &entry{
- key: k,
- hash: h,
- }
- en.setValue(v)
- return en
- }
- func (e *entry) getValue() Value {
- return e.value.Load().(Value)
- }
- func (e *entry) setValue(v Value) {
- e.value.Store(v)
- }
- func (e *entry) getAccessTime() int64 {
- return atomic.LoadInt64(&e.accessTime)
- }
- func (e *entry) setAccessTime(v int64) {
- atomic.StoreInt64(&e.accessTime, v)
- }
- func (e *entry) getWriteTime() int64 {
- return atomic.LoadInt64(&e.writeTime)
- }
- func (e *entry) setWriteTime(v int64) {
- atomic.StoreInt64(&e.writeTime, v)
- }
- func (e *entry) getLoading() bool {
- return atomic.LoadInt32(&e.loading) != 0
- }
- func (e *entry) setLoading(v bool) bool {
- if v {
- return atomic.CompareAndSwapInt32(&e.loading, 0, 1)
- }
- return atomic.CompareAndSwapInt32(&e.loading, 1, 0)
- }
- func (e *entry) getInvalidated() bool {
- return atomic.LoadInt32(&e.invalidated) != 0
- }
- func (e *entry) setInvalidated(v bool) {
- if v {
- atomic.StoreInt32(&e.invalidated, 1)
- } else {
- atomic.StoreInt32(&e.invalidated, 0)
- }
- }
- // getEntry returns the entry attached to the given list element.
- func getEntry(el *list.Element) *entry {
- return el.Value.(*entry)
- }
- // event is the cache event (add, hit or delete).
- type event uint8
- const (
- eventWrite event = iota
- eventAccess
- eventDelete
- eventClose
- )
- type entryEvent struct {
- entry *entry
- event event
- }
- // cache is a data structure for cache entries.
- type cache struct {
- size int64 // Access atomically - must be aligned on 32-bit
- segs [segmentCount]sync.Map // map[Key]*entry
- }
- func (c *cache) get(k Key, h uint64) *entry {
- seg := c.segment(h)
- v, ok := seg.Load(k)
- if ok {
- return v.(*entry)
- }
- return nil
- }
- func (c *cache) getOrSet(v *entry) *entry {
- seg := c.segment(v.hash)
- en, ok := seg.LoadOrStore(v.key, v)
- if ok {
- return en.(*entry)
- }
- atomic.AddInt64(&c.size, 1)
- return nil
- }
- func (c *cache) delete(v *entry) {
- seg := c.segment(v.hash)
- seg.Delete(v.key)
- atomic.AddInt64(&c.size, -1)
- }
- func (c *cache) len() int {
- return int(atomic.LoadInt64(&c.size))
- }
- func (c *cache) walk(fn func(*entry)) {
- for i := range c.segs {
- c.segs[i].Range(func(k, v interface{}) bool {
- fn(v.(*entry))
- return true
- })
- }
- }
- func (c *cache) segment(h uint64) *sync.Map {
- return &c.segs[h&segmentMask]
- }
- // policy is a cache policy.
- type policy interface {
- // init initializes the policy.
- init(cache *cache, maximumSize int)
- // write handles Write event for the entry.
- // It adds new entry and returns evicted entry if needed.
- write(entry *entry) *entry
- // access handles Access event for the entry.
- // It marks then entry recently accessed.
- access(entry *entry)
- // remove removes the entry.
- remove(entry *entry) *entry
- // iterate iterates all entries by their access time.
- iterate(func(entry *entry) bool)
- }
- func newPolicy(name string) policy {
- switch name {
- case "", "slru":
- return &slruCache{}
- case "lru":
- return &lruCache{}
- case "tinylfu":
- return &tinyLFU{}
- default:
- panic("cache: unsupported policy " + name)
- }
- }
- // recencyQueue manages cache entries by write time.
- type recencyQueue struct {
- ls list.List
- }
- func (w *recencyQueue) init(cache *cache, maximumSize int) {
- w.ls.Init()
- }
- func (w *recencyQueue) write(en *entry) *entry {
- if en.writeList == nil {
- en.writeList = w.ls.PushFront(en)
- } else {
- w.ls.MoveToFront(en.writeList)
- }
- return nil
- }
- func (w *recencyQueue) access(en *entry) {
- }
- func (w *recencyQueue) remove(en *entry) *entry {
- if en.writeList == nil {
- return en
- }
- w.ls.Remove(en.writeList)
- en.writeList = nil
- return en
- }
- func (w *recencyQueue) iterate(fn func(en *entry) bool) {
- iterateListFromBack(&w.ls, fn)
- }
- type discardingQueue struct{}
- func (discardingQueue) init(cache *cache, maximumSize int) {
- }
- func (discardingQueue) write(en *entry) *entry {
- return nil
- }
- func (discardingQueue) access(en *entry) {
- }
- func (discardingQueue) remove(en *entry) *entry {
- return en
- }
- func (discardingQueue) iterate(fn func(en *entry) bool) {
- }
- func iterateListFromBack(ls *list.List, fn func(en *entry) bool) {
- for el := ls.Back(); el != nil; {
- en := getEntry(el)
- prev := el.Prev() // Get Prev as fn can delete the entry.
- if !fn(en) {
- return
- }
- el = prev
- }
- }
|