6a2a075c14
This was causing `fatal error: sync: unlock of unlocked mutex` if a panic ocurred in fsNewFs.
94 lines
2.1 KiB
Go
94 lines
2.1 KiB
Go
// Package cache implements the Fs cache
|
|
package cache
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/ncw/rclone/fs"
|
|
)
|
|
|
|
var (
|
|
fsCacheMu sync.Mutex
|
|
fsCache = map[string]*cacheEntry{}
|
|
fsNewFs = fs.NewFs // for tests
|
|
expireRunning = false
|
|
cacheExpireDuration = 300 * time.Second // expire the cache entry when it is older than this
|
|
cacheExpireInterval = 60 * time.Second // interval to run the cache expire
|
|
)
|
|
|
|
type cacheEntry struct {
|
|
f fs.Fs // cached f
|
|
err error // nil or fs.ErrorIsFile
|
|
fsString string // remote string
|
|
lastUsed time.Time // time used for expiry
|
|
}
|
|
|
|
// Get gets a fs.Fs named fsString either from the cache or creates it afresh
|
|
func Get(fsString string) (f fs.Fs, err error) {
|
|
fsCacheMu.Lock()
|
|
entry, ok := fsCache[fsString]
|
|
if !ok {
|
|
fsCacheMu.Unlock() // Unlock in case Get is called recursively
|
|
f, err = fsNewFs(fsString)
|
|
if err != nil && err != fs.ErrorIsFile {
|
|
return f, err
|
|
}
|
|
entry = &cacheEntry{
|
|
f: f,
|
|
fsString: fsString,
|
|
err: err,
|
|
}
|
|
fsCacheMu.Lock()
|
|
fsCache[fsString] = entry
|
|
}
|
|
defer fsCacheMu.Unlock()
|
|
entry.lastUsed = time.Now()
|
|
if !expireRunning {
|
|
time.AfterFunc(cacheExpireInterval, cacheExpire)
|
|
expireRunning = true
|
|
}
|
|
return entry.f, entry.err
|
|
}
|
|
|
|
// Put puts an fs.Fs named fsString into the cache
|
|
func Put(fsString string, f fs.Fs) {
|
|
fsCacheMu.Lock()
|
|
defer fsCacheMu.Unlock()
|
|
fsCache[fsString] = &cacheEntry{
|
|
f: f,
|
|
fsString: fsString,
|
|
lastUsed: time.Now(),
|
|
}
|
|
if !expireRunning {
|
|
time.AfterFunc(cacheExpireInterval, cacheExpire)
|
|
expireRunning = true
|
|
}
|
|
}
|
|
|
|
// cacheExpire expires any entries that haven't been used recently
|
|
func cacheExpire() {
|
|
fsCacheMu.Lock()
|
|
defer fsCacheMu.Unlock()
|
|
now := time.Now()
|
|
for fsString, entry := range fsCache {
|
|
if now.Sub(entry.lastUsed) > cacheExpireDuration {
|
|
delete(fsCache, fsString)
|
|
}
|
|
}
|
|
if len(fsCache) != 0 {
|
|
time.AfterFunc(cacheExpireInterval, cacheExpire)
|
|
expireRunning = true
|
|
} else {
|
|
expireRunning = false
|
|
}
|
|
}
|
|
|
|
// Clear removes everything from the cahce
|
|
func Clear() {
|
|
fsCacheMu.Lock()
|
|
for k := range fsCache {
|
|
delete(fsCache, k)
|
|
}
|
|
fsCacheMu.Unlock()
|
|
}
|