From b7c7652ad2fa6908406fa5da21bc8577e8458103 Mon Sep 17 00:00:00 2001
From: Han-Wen Nienhuys <hanwen@google.com>
Date: Fri, 29 Apr 2011 11:17:30 -0300
Subject: [PATCH] Cleanup CachingFileSystem.

---
 unionfs/cachingfs.go | 134 ++++++++++++++++++-------------------------
 1 file changed, 56 insertions(+), 78 deletions(-)

diff --git a/unionfs/cachingfs.go b/unionfs/cachingfs.go
index e2b4716..f685a17 100644
--- a/unionfs/cachingfs.go
+++ b/unionfs/cachingfs.go
@@ -2,118 +2,96 @@ package unionfs
 
 import (
 	"github.com/hanwen/go-fuse/fuse"
-	"sync"
 )
 
 type attrResponse struct {
-	attr *fuse.Attr
-	code fuse.Status
+	*fuse.Attr
+	fuse.Status
 }
 
 type dirResponse struct {
 	entries []fuse.DirEntry
-	code    fuse.Status
+	fuse.Status
 }
 
 type linkResponse struct {
 	linkContent string
-	code        fuse.Status
+	fuse.Status
 }
 
 // Caches readdir and getattr()
 type CachingFileSystem struct {
 	fuse.FileSystem
 
-	attributesLock sync.RWMutex
-	attributes     map[string]attrResponse
-
-	dirsLock sync.RWMutex
-	dirs     map[string]dirResponse
-
-	linksLock sync.RWMutex
-	links     map[string]linkResponse
+	attributes *TimedCache
+	dirs       *TimedCache
+	links      *TimedCache
 }
 
-func NewCachingFileSystem(pfs fuse.FileSystem) *CachingFileSystem {
-	c := new(CachingFileSystem)
-	c.FileSystem = pfs
-	c.attributes = make(map[string]attrResponse)
-	c.dirs = make(map[string]dirResponse)
-	c.links = make(map[string]linkResponse)
-	return c
-}
-
-func (me *CachingFileSystem) GetAttr(name string) (*fuse.Attr, fuse.Status) {
-	me.attributesLock.RLock()
-	v, ok := me.attributes[name]
-	me.attributesLock.RUnlock()
+func readDir(fs fuse.FileSystem, name string) *dirResponse {
+	origStream, code := fs.OpenDir(name)
 
-	if ok {
-		return v.attr, v.code
+	r := &dirResponse{nil, code}
+	if code != fuse.OK {
+		return r
 	}
 
-	var r attrResponse
-	r.attr, r.code = me.FileSystem.GetAttr(name)
-
-	// TODO - could do async.
-	me.attributesLock.Lock()
-	me.attributes[name] = r
-	me.attributesLock.Unlock()
+	for {
+		d, ok := <-origStream
+		if !ok {
+			break
+		}
+		r.entries = append(r.entries, d)
+	}
 
-	return r.attr, r.code
+	return r
 }
 
-func (me *CachingFileSystem) Readlink(name string) (string, fuse.Status) {
-	me.linksLock.RLock()
-	v, ok := me.links[name]
-	me.linksLock.RUnlock()
+func getAttr(fs fuse.FileSystem, name string) *attrResponse {
+	a, code := fs.GetAttr(name)
+	return &attrResponse{
+		Attr:   a,
+		Status: code,
+	}
+}
 
-	if ok {
-		return v.linkContent, v.code
+func readLink(fs fuse.FileSystem, name string) *linkResponse {
+	a, code := fs.Readlink(name)
+	return &linkResponse{
+		linkContent: a,
+		Status:      code,
 	}
+}
 
-	v.linkContent, v.code = me.FileSystem.Readlink(name)
+func NewCachingFileSystem(fs fuse.FileSystem, ttlNs int64) *CachingFileSystem {
+	c := new(CachingFileSystem)
+	c.FileSystem = fs
+	c.attributes = NewTimedCache(func(n string) interface{} { return getAttr(fs, n) }, ttlNs)
+	c.dirs = NewTimedCache(func(n string) interface{} { return readDir(fs, n) }, ttlNs)
+	c.links = NewTimedCache(func(n string) interface{} { return readLink(fs, n) }, ttlNs)
+	return c
+}
 
-	// TODO - could do async.
-	me.linksLock.Lock()
-	me.links[name] = v
-	me.linksLock.Unlock()
+func (me *CachingFileSystem) GetAttr(name string) (*fuse.Attr, fuse.Status) {
+	r := me.attributes.Get(name).(attrResponse)
+	return r.Attr, r.Status
+}
 
-	return v.linkContent, v.code
+func (me *CachingFileSystem) Readlink(name string) (string, fuse.Status) {
+	r := me.attributes.Get(name).(linkResponse)
+	return r.linkContent, r.Status
 }
 
 func (me *CachingFileSystem) OpenDir(name string) (stream chan fuse.DirEntry, status fuse.Status) {
-	me.dirsLock.RLock()
-	v, ok := me.dirs[name]
-	me.dirsLock.RUnlock()
-
-	if !ok {
-		origStream, code := me.FileSystem.OpenDir(name)
-		if code != fuse.OK {
-			return nil, code
-		}
-
-		v.code = code
-		for {
-			d := <-origStream
-			if d.Name == "" {
-				break
-			}
-			v.entries = append(v.entries, d)
-		}
-
-		me.dirsLock.Lock()
-		me.dirs[name] = v
-		me.dirsLock.Unlock()
-	}
-
-	stream = make(chan fuse.DirEntry)
-	go func() {
-		for _, d := range v.entries {
+	r := me.dirs.Get(name).(dirResponse)
+	if r.Status == fuse.OK {
+		stream = make(chan fuse.DirEntry, len(r.entries))
+		for _, d := range r.entries {
 			stream <- d
 		}
-		stream <- fuse.DirEntry{}
-	}()
+		close(stream)
+		return stream, r.Status
+	}
 
-	return stream, v.code
+	return nil, r.Status
 }
-- 
2.30.9