github.com/ewagmig/fabric@v2.1.1+incompatible/msp/cache/second_chance.go (about) 1 /* 2 Copyright IBM Corp. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package cache 8 9 import ( 10 "sync" 11 "sync/atomic" 12 ) 13 14 // This package implements Second-Chance Algorithm, an approximate LRU algorithms. 15 // https://www.cs.jhu.edu/~yairamir/cs418/os6/tsld023.htm 16 17 // secondChanceCache holds key-value items with a limited size. 18 // When the number cached items exceeds the limit, victims are selected based on the 19 // Second-Chance Algorithm and get purged 20 type secondChanceCache struct { 21 // manages mapping between keys and items 22 table map[string]*cacheItem 23 24 // holds a list of cached items. 25 items []*cacheItem 26 27 // indicates the next candidate of a victim in the items list 28 position int 29 30 // read lock for get, and write lock for add 31 rwlock sync.RWMutex 32 } 33 34 type cacheItem struct { 35 key string 36 value interface{} 37 // set to 1 when get() is called. set to 0 when victim scan 38 referenced int32 39 } 40 41 func newSecondChanceCache(cacheSize int) *secondChanceCache { 42 var cache secondChanceCache 43 cache.position = 0 44 cache.items = make([]*cacheItem, cacheSize) 45 cache.table = make(map[string]*cacheItem) 46 47 return &cache 48 } 49 50 func (cache *secondChanceCache) len() int { 51 cache.rwlock.RLock() 52 defer cache.rwlock.RUnlock() 53 54 return len(cache.table) 55 } 56 57 func (cache *secondChanceCache) get(key string) (interface{}, bool) { 58 cache.rwlock.RLock() 59 defer cache.rwlock.RUnlock() 60 61 item, ok := cache.table[key] 62 if !ok { 63 return nil, false 64 } 65 66 // referenced bit is set to true to indicate that this item is recently accessed. 67 atomic.StoreInt32(&item.referenced, 1) 68 69 return item.value, true 70 } 71 72 func (cache *secondChanceCache) add(key string, value interface{}) { 73 cache.rwlock.Lock() 74 defer cache.rwlock.Unlock() 75 76 if old, ok := cache.table[key]; ok { 77 old.value = value 78 atomic.StoreInt32(&old.referenced, 1) 79 return 80 } 81 82 var item cacheItem 83 item.key = key 84 item.value = value 85 atomic.StoreInt32(&item.referenced, 1) 86 87 size := len(cache.items) 88 num := len(cache.table) 89 if num < size { 90 // cache is not full, so just store the new item at the end of the list 91 cache.table[key] = &item 92 cache.items[num] = &item 93 return 94 } 95 96 // starts victim scan since cache is full 97 for { 98 // checks whether this item is recently accsessed or not 99 victim := cache.items[cache.position] 100 if atomic.LoadInt32(&victim.referenced) == 0 { 101 // a victim is found. delete it, and store the new item here. 102 delete(cache.table, victim.key) 103 cache.table[key] = &item 104 cache.items[cache.position] = &item 105 cache.position = (cache.position + 1) % size 106 return 107 } 108 109 // referenced bit is set to false so that this item will be get purged 110 // unless it is accessed until a next victim scan 111 atomic.StoreInt32(&victim.referenced, 0) 112 cache.position = (cache.position + 1) % size 113 } 114 }