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  }