github.com/tetratelabs/wazero@v1.2.1/internal/filecache/file_cache.go (about) 1 package filecache 2 3 import ( 4 "encoding/hex" 5 "errors" 6 "io" 7 "os" 8 "path" 9 "sync" 10 ) 11 12 // New returns a new Cache implemented by fileCache. 13 func New(dir string) Cache { 14 return newFileCache(dir) 15 } 16 17 func newFileCache(dir string) *fileCache { 18 return &fileCache{dirPath: dir} 19 } 20 21 // fileCache persists compiled functions into dirPath. 22 // 23 // Note: this can be expanded to do binary signing/verification, set TTL on each entry, etc. 24 type fileCache struct { 25 dirPath string 26 mux sync.RWMutex 27 } 28 29 type fileReadCloser struct { 30 *os.File 31 fc *fileCache 32 } 33 34 func (fc *fileCache) path(key Key) string { 35 return path.Join(fc.dirPath, hex.EncodeToString(key[:])) 36 } 37 38 func (fc *fileCache) Get(key Key) (content io.ReadCloser, ok bool, err error) { 39 // TODO: take lock per key for more efficiency vs the complexity of impl. 40 fc.mux.RLock() 41 unlock := fc.mux.RUnlock 42 defer func() { 43 if unlock != nil { 44 unlock() 45 } 46 }() 47 48 f, err := os.Open(fc.path(key)) 49 if errors.Is(err, os.ErrNotExist) { 50 return nil, false, nil 51 } else if err != nil { 52 return nil, false, err 53 } else { 54 // Unlock is done inside the content.Close() at the call site. 55 unlock = nil 56 return &fileReadCloser{File: f, fc: fc}, true, nil 57 } 58 } 59 60 // Close wraps the os.File Close to release the read lock on fileCache. 61 func (f *fileReadCloser) Close() (err error) { 62 defer f.fc.mux.RUnlock() 63 err = f.File.Close() 64 return 65 } 66 67 func (fc *fileCache) Add(key Key, content io.Reader) (err error) { 68 // TODO: take lock per key for more efficiency vs the complexity of impl. 69 fc.mux.Lock() 70 defer fc.mux.Unlock() 71 72 file, err := os.Create(fc.path(key)) 73 if err != nil { 74 return 75 } 76 defer file.Close() 77 _, err = io.Copy(file, content) 78 return 79 } 80 81 func (fc *fileCache) Delete(key Key) (err error) { 82 // TODO: take lock per key for more efficiency vs the complexity of impl. 83 fc.mux.Lock() 84 defer fc.mux.Unlock() 85 86 err = os.Remove(fc.path(key)) 87 if errors.Is(err, os.ErrNotExist) { 88 err = nil 89 } 90 return 91 }