mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 16:34:30 +01:00
vfs: re-use existing VFS if possible
This commit is contained in:
parent
7781ea8d59
commit
2f66355f20
93
vfs/vfs.go
93
vfs/vfs.go
@ -157,23 +157,31 @@ var (
|
|||||||
|
|
||||||
// VFS represents the top level filing system
|
// VFS represents the top level filing system
|
||||||
type VFS struct {
|
type VFS struct {
|
||||||
f fs.Fs
|
f fs.Fs
|
||||||
root *Dir
|
root *Dir
|
||||||
Opt vfscommon.Options
|
Opt vfscommon.Options
|
||||||
cache *vfscache.Cache
|
cache *vfscache.Cache
|
||||||
cancel context.CancelFunc
|
cancelCache context.CancelFunc
|
||||||
usageMu sync.Mutex
|
usageMu sync.Mutex
|
||||||
usageTime time.Time
|
usageTime time.Time
|
||||||
usage *fs.Usage
|
usage *fs.Usage
|
||||||
pollChan chan time.Duration
|
pollChan chan time.Duration
|
||||||
|
inUse int32 // count of number of opens accessed with atomic
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep track of active VFS keyed on fs.ConfigString(f)
|
||||||
|
var (
|
||||||
|
activeMu sync.Mutex
|
||||||
|
active = map[string][]*VFS{}
|
||||||
|
)
|
||||||
|
|
||||||
// New creates a new VFS and root directory. If opt is nil, then
|
// New creates a new VFS and root directory. If opt is nil, then
|
||||||
// DefaultOpt will be used
|
// DefaultOpt will be used
|
||||||
func New(f fs.Fs, opt *vfscommon.Options) *VFS {
|
func New(f fs.Fs, opt *vfscommon.Options) *VFS {
|
||||||
fsDir := fs.NewDir("", time.Now())
|
fsDir := fs.NewDir("", time.Now())
|
||||||
vfs := &VFS{
|
vfs := &VFS{
|
||||||
f: f,
|
f: f,
|
||||||
|
inUse: int32(1),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make a copy of the options
|
// Make a copy of the options
|
||||||
@ -190,6 +198,20 @@ func New(f fs.Fs, opt *vfscommon.Options) *VFS {
|
|||||||
// Make sure directories are returned as directories
|
// Make sure directories are returned as directories
|
||||||
vfs.Opt.DirPerms |= os.ModeDir
|
vfs.Opt.DirPerms |= os.ModeDir
|
||||||
|
|
||||||
|
// Find a VFS with the same name and options and return it if possible
|
||||||
|
activeMu.Lock()
|
||||||
|
defer activeMu.Unlock()
|
||||||
|
configName := fs.ConfigString(f)
|
||||||
|
for _, activeVFS := range active[configName] {
|
||||||
|
if vfs.Opt == activeVFS.Opt {
|
||||||
|
fs.Debugf(f, "Re-using VFS from active cache")
|
||||||
|
atomic.AddInt32(&activeVFS.inUse, 1)
|
||||||
|
return activeVFS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Put the VFS into the active cache
|
||||||
|
active[configName] = append(active[configName], vfs)
|
||||||
|
|
||||||
// Create root directory
|
// Create root directory
|
||||||
vfs.root = newDir(vfs, f, nil, fsDir)
|
vfs.root = newDir(vfs, f, nil, fsDir)
|
||||||
|
|
||||||
@ -208,9 +230,24 @@ func New(f fs.Fs, opt *vfscommon.Options) *VFS {
|
|||||||
// with the same remote string we get this one. The Pin is
|
// with the same remote string we get this one. The Pin is
|
||||||
// removed by Shutdown
|
// removed by Shutdown
|
||||||
cache.Pin(f)
|
cache.Pin(f)
|
||||||
|
|
||||||
return vfs
|
return vfs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the number of active cache entries and a VFS if any are in
|
||||||
|
// the cache.
|
||||||
|
func activeCacheEntries() (vfs *VFS, count int) {
|
||||||
|
activeMu.Lock()
|
||||||
|
for _, vfses := range active {
|
||||||
|
count += len(vfses)
|
||||||
|
if len(vfses) > 0 {
|
||||||
|
vfs = vfses[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
activeMu.Unlock()
|
||||||
|
return vfs, count
|
||||||
|
}
|
||||||
|
|
||||||
// Fs returns the Fs passed into the New call
|
// Fs returns the Fs passed into the New call
|
||||||
func (vfs *VFS) Fs() fs.Fs {
|
func (vfs *VFS) Fs() fs.Fs {
|
||||||
return vfs.f
|
return vfs.f
|
||||||
@ -218,7 +255,7 @@ func (vfs *VFS) Fs() fs.Fs {
|
|||||||
|
|
||||||
// SetCacheMode change the cache mode
|
// SetCacheMode change the cache mode
|
||||||
func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) {
|
func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) {
|
||||||
vfs.Shutdown()
|
vfs.shutdownCache()
|
||||||
vfs.cache = nil
|
vfs.cache = nil
|
||||||
if cacheMode > vfscommon.CacheModeOff {
|
if cacheMode > vfscommon.CacheModeOff {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@ -230,19 +267,43 @@ func (vfs *VFS) SetCacheMode(cacheMode vfscommon.CacheMode) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
vfs.Opt.CacheMode = cacheMode
|
vfs.Opt.CacheMode = cacheMode
|
||||||
vfs.cancel = cancel
|
vfs.cancelCache = cancel
|
||||||
vfs.cache = cache
|
vfs.cache = cache
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown stops any background go-routines
|
// shutdown the cache if it was running
|
||||||
|
func (vfs *VFS) shutdownCache() {
|
||||||
|
if vfs.cancelCache != nil {
|
||||||
|
vfs.cancelCache()
|
||||||
|
vfs.cancelCache = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown stops any background go-routines and removes the VFS from
|
||||||
|
// the active ache.
|
||||||
func (vfs *VFS) Shutdown() {
|
func (vfs *VFS) Shutdown() {
|
||||||
|
if atomic.AddInt32(&vfs.inUse, -1) > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Unpin the Fs from the cache
|
// Unpin the Fs from the cache
|
||||||
cache.Unpin(vfs.f)
|
cache.Unpin(vfs.f)
|
||||||
if vfs.cancel != nil {
|
|
||||||
vfs.cancel()
|
// Remove from active cache
|
||||||
vfs.cancel = nil
|
activeMu.Lock()
|
||||||
|
configName := fs.ConfigString(vfs.f)
|
||||||
|
activeVFSes := active[configName]
|
||||||
|
for i, activeVFS := range activeVFSes {
|
||||||
|
if activeVFS == vfs {
|
||||||
|
activeVFSes[i] = nil
|
||||||
|
active[configName] = append(activeVFSes[:i], activeVFSes[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
activeMu.Unlock()
|
||||||
|
|
||||||
|
vfs.shutdownCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CleanUp deletes the contents of the on disk cache
|
// CleanUp deletes the contents of the on disk cache
|
||||||
|
@ -128,14 +128,39 @@ func TestVFSbaseHandle(t *testing.T) {
|
|||||||
|
|
||||||
// TestNew sees if the New command works properly
|
// TestNew sees if the New command works properly
|
||||||
func TestVFSNew(t *testing.T) {
|
func TestVFSNew(t *testing.T) {
|
||||||
|
// Check active cache has this many entries
|
||||||
|
checkActiveCacheEntries := func(i int) {
|
||||||
|
_, count := activeCacheEntries()
|
||||||
|
assert.Equal(t, i, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkActiveCacheEntries(0)
|
||||||
|
|
||||||
r, vfs, cleanup := newTestVFS(t)
|
r, vfs, cleanup := newTestVFS(t)
|
||||||
defer cleanup()
|
|
||||||
|
|
||||||
// Check making a VFS with nil options
|
// Check making a VFS with nil options
|
||||||
var defaultOpt = vfscommon.DefaultOpt
|
var defaultOpt = vfscommon.DefaultOpt
|
||||||
defaultOpt.DirPerms |= os.ModeDir
|
defaultOpt.DirPerms |= os.ModeDir
|
||||||
assert.Equal(t, vfs.Opt, defaultOpt)
|
assert.Equal(t, vfs.Opt, defaultOpt)
|
||||||
assert.Equal(t, vfs.f, r.Fremote)
|
assert.Equal(t, vfs.f, r.Fremote)
|
||||||
|
|
||||||
|
checkActiveCacheEntries(1)
|
||||||
|
|
||||||
|
// Check that we get the same VFS if we ask for it again with
|
||||||
|
// the same options
|
||||||
|
vfs2 := New(r.Fremote, nil)
|
||||||
|
assert.Equal(t, fmt.Sprintf("%p", vfs), fmt.Sprintf("%p", vfs2))
|
||||||
|
|
||||||
|
checkActiveCacheEntries(1)
|
||||||
|
|
||||||
|
// Shut the new VFS down and check the cache still has stuff in
|
||||||
|
vfs2.Shutdown()
|
||||||
|
|
||||||
|
checkActiveCacheEntries(1)
|
||||||
|
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
checkActiveCacheEntries(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestNew sees if the New command works properly
|
// TestNew sees if the New command works properly
|
||||||
|
Loading…
Reference in New Issue
Block a user