Ver código fonte

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

Simon Frei 4 anos atrás
pai
commit
08e3cd1cce
1 arquivos alterados com 45 adições e 47 exclusões
  1. 45 47
      lib/fs/casefs.go

+ 45 - 47
lib/fs/casefs.go

@@ -386,7 +386,6 @@ func (f *caseFilesystem) checkCaseExisting(name string) error {
 }
 
 type defaultRealCaser struct {
-	fs    Filesystem
 	cache caseCache
 }
 
@@ -396,13 +395,12 @@ func newDefaultRealCaser(fs Filesystem) *defaultRealCaser {
 	if err != nil {
 		panic(err)
 	}
-	caser := &defaultRealCaser{
-		fs: fs,
+	return &defaultRealCaser{
 		cache: caseCache{
+			fs:            fs,
 			TwoQueueCache: cache,
 		},
 	}
-	return caser
 }
 
 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) {
 		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 {
 			return "", node.err
 		}
@@ -457,10 +434,29 @@ func (r *defaultRealCaser) dropCache() {
 	r.cache.Purge()
 }
 
-func newCaseNode() *caseNode {
-	return &caseNode{
-		expires: time.Now().Add(caseCacheTimeout),
+type caseCache struct {
+	*lru.TwoQueueCache
+	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
@@ -474,30 +470,32 @@ type caseNode struct {
 	expires     time.Time
 	lowerToReal map[string]string
 	children    map[string]struct{}
-	once        sync.Once
 	err         error
 }
 
-type caseCache struct {
-	*lru.TwoQueueCache
-	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()
-		c.Add(key, node)
+func newCaseNode(name string, filesystem Filesystem) *caseNode {
+	node := new(caseNode)
+	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
 	}
-	node := v.(*caseNode)
-	if node.expires.Before(time.Now()) {
-		node = newCaseNode()
-		c.Add(key, node)
+
+	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
+		}
 	}
+
 	return node
 }