From ddfde6814073a136584aa06d4a3fce5fbb518fb0 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Wed, 8 Jul 2020 10:27:26 +0100 Subject: [PATCH] vfs: fix directory locking caused by slow directory listings Before this fix we took the directory lock to read the ModTime of the directory. This was causing locking on directories which were being re-read from the backend. This commit gives the modtime its own lock so it can be read even when the directory is being updated. See: https://forum.rclone.org/t/high-cpu-load-with-rclone-mount/17604 --- vfs/dir.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/vfs/dir.go b/vfs/dir.go index a6889eb4c..972f75b23 100644 --- a/vfs/dir.go +++ b/vfs/dir.go @@ -29,12 +29,14 @@ type Dir struct { mu sync.RWMutex // protects the following parent *Dir // parent, nil for root path string - modTime time.Time entry fs.Directory read time.Time // time directory entry last read items map[string]Node // directory entries - can be empty but not nil virtual map[string]vState // virtual directory entries - may be nil sys atomic.Value // user defined info to be attached here + + modTimeMu sync.Mutex // protects the following + modTime time.Time } //go:generate stringer -type=vState @@ -270,11 +272,13 @@ func (d *Dir) _age(when time.Time) (age time.Duration, stale bool) { // reading everything again func (d *Dir) rename(newParent *Dir, fsDir fs.Directory) { d.ForgetAll() + d.modTimeMu.Lock() + d.modTime = fsDir.ModTime(context.TODO()) + d.modTimeMu.Unlock() d.mu.Lock() d.parent = newParent d.entry = fsDir d.path = fsDir.Remote() - d.modTime = fsDir.ModTime(context.TODO()) d.read = time.Time{} d.mu.Unlock() } @@ -550,8 +554,8 @@ func (d *Dir) isEmpty() (bool, error) { // ModTime returns the modification time of the directory func (d *Dir) ModTime() time.Time { - d.mu.RLock() - defer d.mu.RUnlock() + d.modTimeMu.Lock() + defer d.modTimeMu.Unlock() // fs.Debugf(d.path, "Dir.ModTime %v", d.modTime) return d.modTime } @@ -566,9 +570,9 @@ func (d *Dir) SetModTime(modTime time.Time) error { if d.vfs.Opt.ReadOnly { return EROFS } - d.mu.Lock() + d.modTimeMu.Lock() d.modTime = modTime - d.mu.Unlock() + d.modTimeMu.Unlock() return nil }