Files
wazero/internal/filecache/file_cache.go
Takeshi Yoneda 35500f9b85 Introduces Cache API (#1016)
This introduces the new API wazero.Cache interface which can be passed to wazero.RuntimeConfig. 
Users can configure this to share the underlying compilation cache across multiple wazero.Runtime. 
And along the way, this deletes the experimental file cache API as it's replaced by this new API.

Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Co-authored-by: Crypt Keeper <64215+codefromthecrypt@users.noreply.github.com>
2023-01-10 09:32:42 +09:00

92 lines
1.9 KiB
Go

package filecache
import (
"encoding/hex"
"errors"
"io"
"os"
"path"
"sync"
)
// New returns a new Cache implemented by fileCache.
func New(dir string) Cache {
return newFileCache(dir)
}
func newFileCache(dir string) *fileCache {
return &fileCache{dirPath: dir}
}
// fileCache persists compiled functions into dirPath.
//
// Note: this can be expanded to do binary signing/verification, set TTL on each entry, etc.
type fileCache struct {
dirPath string
mux sync.RWMutex
}
type fileReadCloser struct {
*os.File
fc *fileCache
}
func (fc *fileCache) path(key Key) string {
return path.Join(fc.dirPath, hex.EncodeToString(key[:]))
}
func (fc *fileCache) Get(key Key) (content io.ReadCloser, ok bool, err error) {
// TODO: take lock per key for more efficiency vs the complexity of impl.
fc.mux.RLock()
unlock := fc.mux.RUnlock
defer func() {
if unlock != nil {
unlock()
}
}()
f, err := os.Open(fc.path(key))
if errors.Is(err, os.ErrNotExist) {
return nil, false, nil
} else if err != nil {
return nil, false, err
} else {
// Unlock is done inside the content.Close() at the call site.
unlock = nil
return &fileReadCloser{File: f, fc: fc}, true, nil
}
}
// Close wraps the os.File Close to release the read lock on fileCache.
func (f *fileReadCloser) Close() (err error) {
defer f.fc.mux.RUnlock()
err = f.File.Close()
return
}
func (fc *fileCache) Add(key Key, content io.Reader) (err error) {
// TODO: take lock per key for more efficiency vs the complexity of impl.
fc.mux.Lock()
defer fc.mux.Unlock()
file, err := os.Create(fc.path(key))
if err != nil {
return
}
defer file.Close()
_, err = io.Copy(file, content)
return
}
func (fc *fileCache) Delete(key Key) (err error) {
// TODO: take lock per key for more efficiency vs the complexity of impl.
fc.mux.Lock()
defer fc.mux.Unlock()
err = os.Remove(fc.path(key))
if errors.Is(err, os.ErrNotExist) {
err = nil
}
return
}