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
This commit is contained in:
Nick Craig-Wood 2020-07-08 10:27:26 +01:00
parent 811b30d116
commit ddfde68140

View file

@ -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
}