vfs: keep virtual directory status accurate and reduce deadlock potential

This changes hasVirtual to an atomic struct variable that's updated on
add or delete from the virtual map.

This keeps it up to date and avoids deadlocks.

Signed-off-by: Anagh Kumar Baranwal <6824881+darthShadow@users.noreply.github.com>
This commit is contained in:
Anagh Kumar Baranwal 2022-11-14 13:07:12 +05:30 committed by Nick Craig-Wood
parent 52e25c43b9
commit aaadb48d48
1 changed files with 20 additions and 4 deletions

View File

@ -38,6 +38,8 @@ type Dir struct {
modTimeMu sync.Mutex // protects the following
modTime time.Time
_hasVirtual atomic.Bool // shows if the directory has virtual entries
}
//go:generate stringer -type=vState
@ -64,6 +66,7 @@ func newDir(vfs *VFS, f fs.Fs, parent *Dir, fsDir fs.Directory) *Dir {
items: make(map[string]Node),
}
d.cleanupTimer = time.AfterFunc(vfs.Opt.DirCacheTime*2, d.cacheCleanup)
d.setHasVirtual(false)
return d
}
@ -194,6 +197,16 @@ func (d *Dir) Node() Node {
return d
}
// hasVirtual returns whether the directory has virtual entries
func (d *Dir) hasVirtual() bool {
return d._hasVirtual.Load()
}
// setHasVirtual sets the hasVirtual flag for the directory
func (d *Dir) setHasVirtual(hasVirtual bool) {
d._hasVirtual.Store(hasVirtual)
}
// ForgetAll forgets directory entries for this directory and any children.
//
// It does not invalidate or clear the cache of the parent directory.
@ -208,7 +221,7 @@ func (d *Dir) ForgetAll() (hasVirtual bool) {
for _, node := range d.items {
if dir, ok := node.(*Dir); ok {
if dir.ForgetAll() {
hasVirtual = true
d.setHasVirtual(true)
}
}
}
@ -225,17 +238,17 @@ func (d *Dir) ForgetAll() (hasVirtual bool) {
// Check if this dir has virtual entries
if len(d.virtual) != 0 {
hasVirtual = true
d.setHasVirtual(true)
}
// Don't clear directory entries if there are virtual entries in this
// directory or any children
if !hasVirtual {
if !d.hasVirtual() {
d.items = make(map[string]Node)
d.cleanupTimer.Stop()
}
return hasVirtual
return d.hasVirtual()
}
// forgetDirPath clears the cache for itself and all subdirectories if
@ -422,6 +435,7 @@ func (d *Dir) addObject(node Node) {
vAdd = vAddDir
}
d.virtual[leaf] = vAdd
d.setHasVirtual(true)
fs.Debugf(d.path, "Added virtual directory entry %v: %q", vAdd, leaf)
d.mu.Unlock()
}
@ -466,6 +480,7 @@ func (d *Dir) delObject(leaf string) {
d.virtual = make(map[string]vState)
}
d.virtual[leaf] = vDel
d.setHasVirtual(true)
fs.Debugf(d.path, "Added virtual directory entry %v: %q", vDel, leaf)
d.mu.Unlock()
}
@ -525,6 +540,7 @@ func (d *Dir) _deleteVirtual(name string) {
delete(d.virtual, name)
if len(d.virtual) == 0 {
d.virtual = nil
d.setHasVirtual(false)
}
fs.Debugf(d.path, "Removed virtual directory entry %v: %q", virtualState, name)
}