lib/fs: Set expiry after DirNames in case-fs (#7794)

This commit is contained in:
Simon Frei 2021-06-27 08:30:02 +02:00 committed by GitHub
parent da0e5edbec
commit 08e3cd1cce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -386,7 +386,6 @@ func (f *caseFilesystem) checkCaseExisting(name string) error {
} }
type defaultRealCaser struct { type defaultRealCaser struct {
fs Filesystem
cache caseCache cache caseCache
} }
@ -396,13 +395,12 @@ func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
if err != nil { if err != nil {
panic(err) panic(err)
} }
caser := &defaultRealCaser{ return &defaultRealCaser{
fs: fs,
cache: caseCache{ cache: caseCache{
fs: fs,
TwoQueueCache: cache, TwoQueueCache: cache,
}, },
} }
return caser
} }
func (r *defaultRealCaser) realCase(name string) (string, error) { func (r *defaultRealCaser) realCase(name string) (string, error) {
@ -414,27 +412,6 @@ func (r *defaultRealCaser) realCase(name string) (string, error) {
for _, comp := range PathComponents(name) { for _, comp := range PathComponents(name) {
node := r.cache.getExpireAdd(realName) node := r.cache.getExpireAdd(realName)
node.once.Do(func() {
dirNames, err := r.fs.DirNames(realName)
if err != nil {
r.cache.Remove(realName)
node.err = err
return
}
num := len(dirNames)
node.children = make(map[string]struct{}, num)
node.lowerToReal = make(map[string]string, num)
lastLower := ""
for _, n := range dirNames {
node.children[n] = struct{}{}
lower := UnicodeLowercaseNormalized(n)
if lower != lastLower {
node.lowerToReal[lower] = n
lastLower = n
}
}
})
if node.err != nil { if node.err != nil {
return "", node.err return "", node.err
} }
@ -457,10 +434,29 @@ func (r *defaultRealCaser) dropCache() {
r.cache.Purge() r.cache.Purge()
} }
func newCaseNode() *caseNode { type caseCache struct {
return &caseNode{ *lru.TwoQueueCache
expires: time.Now().Add(caseCacheTimeout), fs Filesystem
mut sync.Mutex
}
// getExpireAdd gets an entry for the given key. If no entry exists, or it is
// expired a new one is created and added to the cache.
func (c *caseCache) getExpireAdd(key string) *caseNode {
c.mut.Lock()
defer c.mut.Unlock()
v, ok := c.Get(key)
if !ok {
node := newCaseNode(key, c.fs)
c.Add(key, node)
return node
} }
node := v.(*caseNode)
if node.expires.Before(time.Now()) {
node = newCaseNode(key, c.fs)
c.Add(key, node)
}
return node
} }
// The keys to children are "real", case resolved names of the path // The keys to children are "real", case resolved names of the path
@ -474,30 +470,32 @@ type caseNode struct {
expires time.Time expires time.Time
lowerToReal map[string]string lowerToReal map[string]string
children map[string]struct{} children map[string]struct{}
once sync.Once
err error err error
} }
type caseCache struct { func newCaseNode(name string, filesystem Filesystem) *caseNode {
*lru.TwoQueueCache node := new(caseNode)
mut sync.Mutex dirNames, err := filesystem.DirNames(name)
} // Set expiry after calling DirNames in case this is super-slow
// (e.g. dirs with many children on android)
node.expires = time.Now().Add(caseCacheTimeout)
if err != nil {
node.err = err
return node
}
// getExpireAdd gets an entry for the given key. If no entry exists, or it is num := len(dirNames)
// expired a new one is created and added to the cache. node.children = make(map[string]struct{}, num)
func (c *caseCache) getExpireAdd(key string) *caseNode { node.lowerToReal = make(map[string]string, num)
c.mut.Lock() lastLower := ""
defer c.mut.Unlock() for _, n := range dirNames {
v, ok := c.Get(key) node.children[n] = struct{}{}
if !ok { lower := UnicodeLowercaseNormalized(n)
node := newCaseNode() if lower != lastLower {
c.Add(key, node) node.lowerToReal[lower] = n
return node lastLower = n
} }
node := v.(*caseNode)
if node.expires.Before(time.Now()) {
node = newCaseNode()
c.Add(key, node)
} }
return node return node
} }