import (
"os"
- "sync"
"time"
)
stale func(time.Time) bool
// internal fields
- staleLock sync.Mutex
- staleAll time.Time
- staleOne map[string]time.Time
+ staleAll time.Time
+ staleOne map[string]time.Time
}
// Sync flushes pending writes for loaded children and, if successful,
if err != nil {
return err
}
- ln.staleLock.Lock()
+ ln.Lock()
ln.staleAll = time.Time{}
ln.staleOne = nil
- ln.staleLock.Unlock()
+ ln.Unlock()
return nil
}
func (ln *lookupnode) Readdir() ([]os.FileInfo, error) {
- ln.staleLock.Lock()
- defer ln.staleLock.Unlock()
+ ln.Lock()
checkTime := time.Now()
if ln.stale(ln.staleAll) {
all, err := ln.loadAll(ln)
if err != nil {
+ ln.Unlock()
return nil, err
}
for _, child := range all {
return child, nil
})
if err != nil {
+ ln.Unlock()
return nil, err
}
}
// newer than ln.staleAll. Reclaim memory.
ln.staleOne = nil
}
+ ln.Unlock()
return ln.treenode.Readdir()
}
+// Child rejects (with ErrInvalidOperation) calls to add/replace
+// children, instead calling loadOne when a non-existing child is
+// looked up.
func (ln *lookupnode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
- ln.staleLock.Lock()
- defer ln.staleLock.Unlock()
checkTime := time.Now()
+ var existing inode
+ var err error
if ln.stale(ln.staleAll) && ln.stale(ln.staleOne[name]) {
- _, err := ln.treenode.Child(name, func(inode) (inode, error) {
+ existing, err = ln.treenode.Child(name, func(inode) (inode, error) {
return ln.loadOne(ln, name)
})
- if err != nil {
- return nil, err
+ if err == nil && existing != nil {
+ if ln.staleOne == nil {
+ ln.staleOne = map[string]time.Time{name: checkTime}
+ } else {
+ ln.staleOne[name] = checkTime
+ }
}
- if ln.staleOne == nil {
- ln.staleOne = map[string]time.Time{name: checkTime}
- } else {
- ln.staleOne[name] = checkTime
+ } else {
+ existing, err = ln.treenode.Child(name, nil)
+ if err != nil && !os.IsNotExist(err) {
+ return existing, err
+ }
+ }
+ if replace != nil {
+ // Let the callback try to delete or replace the
+ // existing node; if it does, return
+ // ErrInvalidOperation.
+ if tryRepl, err := replace(existing); err != nil {
+ // Propagate error from callback
+ return existing, err
+ } else if tryRepl != existing {
+ return existing, ErrInvalidOperation
}
}
- return ln.treenode.Child(name, replace)
+ // Return original error from ln.treenode.Child() (it might be
+ // ErrNotExist).
+ return existing, err
}