github.com/cheng762/platon-go@v1.8.17-0.20190529111256-7deff2d7be26/core/lru/wasm_cache.go (about) 1 package lru 2 3 import ( 4 "github.com/PlatONnetwork/PlatON-Go/common" 5 "github.com/PlatONnetwork/PlatON-Go/life/compiler" 6 "github.com/PlatONnetwork/PlatON-Go/log" 7 "bytes" 8 "encoding/gob" 9 "github.com/hashicorp/golang-lru/simplelru" 10 "github.com/syndtr/goleveldb/leveldb" 11 "path/filepath" 12 "sync" 13 ) 14 15 var ( 16 DefaultWasmCacheSize = 1024 17 wasmCache, _ = NewWasmCache(DefaultWasmCacheSize) 18 DefaultWasmCacheDir = "wasmcache" 19 ) 20 21 type WasmLDBCache struct { 22 lru *simplelru.LRU 23 db *leveldb.DB 24 lock sync.RWMutex 25 } 26 27 type WasmModule struct { 28 Module *compiler.Module 29 FunctionCode []compiler.InterpreterCode 30 } 31 32 func WasmCache() *WasmLDBCache { 33 return wasmCache 34 } 35 36 func SetWasmDB(dataDir string) error { 37 path := filepath.Join(dataDir, DefaultWasmCacheDir) 38 39 db, err := leveldb.OpenFile(path, nil) 40 if err != nil { 41 return err 42 } 43 wasmCache.SetDB(db) 44 return nil 45 } 46 47 func NewWasmCache(size int) (*WasmLDBCache, error) { 48 w := &WasmLDBCache{} 49 50 onEvicted := func(k interface{}, v interface{}) { 51 var addr common.Address 52 var module *WasmModule 53 var ok bool 54 55 if addr, ok = k.(common.Address); !ok { 56 return 57 } 58 59 if module, ok = v.(*WasmModule); !ok { 60 return 61 } 62 if w.db != nil { 63 if ok, err := w.db.Has(addr.Bytes(), nil); err != nil || !ok { 64 buffer := new(bytes.Buffer) 65 enc := gob.NewEncoder(buffer) 66 if err := enc.Encode(module); err != nil { 67 log.Error("encode module err:", err) 68 return 69 } 70 w.db.Put(addr.Bytes(), buffer.Bytes(), nil) 71 } 72 } 73 } 74 75 lru, err := simplelru.NewLRU(size, simplelru.EvictCallback(onEvicted)) 76 77 if err != nil { 78 return nil, err 79 } 80 81 w.lru = lru 82 return w, nil 83 } 84 85 func NewWasmLDBCache(size int, db *leveldb.DB) (*WasmLDBCache, error) { 86 w, err := NewWasmCache(size) 87 if err != nil { 88 return nil, err 89 } 90 w.db = db 91 return w, nil 92 } 93 94 func (w *WasmLDBCache) SetDB(db *leveldb.DB) { 95 w.db = db 96 } 97 98 // Purge is used to completely clear the cache 99 func (w *WasmLDBCache) Purge() { 100 w.lock.Lock() 101 w.lru.Purge() 102 w.lock.Unlock() 103 } 104 105 // Add adds a value to the cache. Returns true if an eviction occurred. 106 func (w *WasmLDBCache) Add(key common.Address, value *WasmModule) bool { 107 w.lock.Lock() 108 defer w.lock.Unlock() 109 return w.lru.Add(key, value) 110 } 111 112 // Get looks up a key's value from the cache. 113 func (w *WasmLDBCache) Get(key common.Address) (*WasmModule, bool) { 114 w.lock.Lock() 115 defer w.lock.Unlock() 116 value, ok := w.lru.Get(key) 117 if !ok { 118 if w.db != nil { 119 if value, err := w.db.Get(key.Bytes(), nil); err == nil { 120 module := WasmModule{} 121 buffer := bytes.NewReader(value) 122 dec := gob.NewDecoder(buffer) 123 if err := dec.Decode(&module); err != nil { 124 log.Error("decode module err:", err) 125 return nil, false 126 } 127 w.lru.Add(key, &module) 128 return &module, true 129 } 130 } 131 return nil, false 132 } 133 return value.(*WasmModule), ok 134 } 135 136 // Check if a key is in the cache, without updating the recent-ness 137 // or deleting it for being stale. 138 func (w *WasmLDBCache) Contains(key common.Address) bool { 139 w.lock.RLock() 140 defer w.lock.RUnlock() 141 if !w.lru.Contains(key) { 142 ok := false 143 if w.db != nil { 144 ok, _ = w.db.Has(key.Bytes(), nil) 145 } 146 return ok 147 } 148 return true 149 } 150 151 // Returns the key value (or undefined if not found) without updating 152 // the "recently used"-ness of the key. 153 func (w *WasmLDBCache) Peek(key common.Address) (*WasmModule, bool) { 154 w.lock.Lock() 155 defer w.lock.Unlock() 156 value, ok := w.lru.Peek(key) 157 if !ok { 158 if w.db != nil { 159 if value, err := w.db.Get(key.Bytes(), nil); err == nil { 160 var module WasmModule 161 buffer := bytes.NewReader(value) 162 dec := gob.NewDecoder(buffer) 163 dec.Decode(module) 164 return &module, true 165 } 166 } 167 return nil, false 168 } 169 return value.(*WasmModule), ok 170 } 171 172 // ContainsOrAdd checks if a key is in the cache without updating the 173 // recent-ness or deleting it for being stale, and if not, adds the value. 174 // Returns whether found and whether an eviction occurred. 175 func (w *WasmLDBCache) ContainsOrAdd(key common.Address, value *WasmModule) (ok, evict bool) { 176 w.lock.Lock() 177 defer w.lock.Unlock() 178 179 if w.lru.Contains(key) { 180 return true, false 181 } else { 182 evict := w.lru.Add(key, value) 183 return false, evict 184 } 185 } 186 187 // Remove removes the provided key from the cache. 188 func (w *WasmLDBCache) Remove(key common.Address) { 189 w.lock.Lock() 190 w.lru.Remove(key) 191 if w.db != nil { 192 w.db.Delete(key.Bytes(), nil) 193 } 194 w.lock.Unlock() 195 } 196 197 // RemoveOldest removes the oldest item from the cache. 198 func (w *WasmLDBCache) RemoveOldest() { 199 w.lock.Lock() 200 w.lru.RemoveOldest() 201 w.lock.Unlock() 202 } 203 204 // Keys returns a slice of the keys in the cache, from oldest to newest. 205 func (w *WasmLDBCache) Keys() []interface{} { 206 w.lock.RLock() 207 defer w.lock.RUnlock() 208 return w.lru.Keys() 209 } 210 211 // Len returns the number of items in the cache. 212 func (w *WasmLDBCache) Len() int { 213 w.lock.RLock() 214 defer w.lock.RUnlock() 215 return w.lru.Len() 216 }