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

View File

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