15397: Remove obsolete APIs.
[arvados.git] / sdk / go / arvados / fs_site.go
index 716ef34e26098771c44f99c46aeea593ce3e87fb..d4f02416822a8b52494f62672a95bd00c1c04519 100644 (file)
@@ -123,6 +123,10 @@ func (fs *customFileSystem) ForwardSlashNameSubstitution(repl string) {
        fs.forwardSlashNameSubstitution = repl
 }
 
+func (fs *customFileSystem) MemorySize() int64 {
+       return fs.fileSystem.MemorySize() + fs.byIDRoot.MemorySize()
+}
+
 // SiteFileSystem returns a FileSystem that maps collections and other
 // Arvados objects onto a filesystem layout.
 //
@@ -153,16 +157,6 @@ func (fs *customFileSystem) newNode(name string, perm os.FileMode, modTime time.
 }
 
 func (fs *customFileSystem) newCollectionOrProjectHardlink(parent inode, id string) (inode, error) {
-       fs.byIDLock.Lock()
-       n := fs.byID[id]
-       if n != nil {
-               // Avoid the extra remote API lookup if we already
-               // have a singleton for this ID.
-               fs.byIDLock.Unlock()
-               return &hardlink{inode: n, parent: parent, name: id}, nil
-       }
-       fs.byIDLock.Unlock()
-
        if strings.Contains(id, "-4zz18-") || pdhRegexp.MatchString(id) {
                node, err := fs.collectionSingleton(id)
                if os.IsNotExist(err) {
@@ -172,13 +166,22 @@ func (fs *customFileSystem) newCollectionOrProjectHardlink(parent inode, id stri
                }
                return &hardlink{inode: node, parent: parent, name: id}, nil
        } else if strings.Contains(id, "-j7d0g-") || strings.Contains(id, "-tpzed-") {
-               proj, err := fs.getProject(id)
-               if os.IsNotExist(err) {
-                       return nil, nil
-               } else if err != nil {
-                       return nil, err
+               fs.byIDLock.Lock()
+               node := fs.byID[id]
+               fs.byIDLock.Unlock()
+               if node == nil {
+                       // Look up the project synchronously before
+                       // calling projectSingleton (otherwise we
+                       // wouldn't detect a nonexistent project until
+                       // it's too late to return ErrNotExist).
+                       proj, err := fs.getProject(id)
+                       if os.IsNotExist(err) {
+                               return nil, nil
+                       } else if err != nil {
+                               return nil, err
+                       }
+                       node = fs.projectSingleton(id, proj)
                }
-               node := fs.projectSingleton(id, proj)
                return &hardlink{inode: node, parent: parent, name: id}, nil
        } else {
                return nil, nil
@@ -242,12 +245,13 @@ func (fs *customFileSystem) getProject(uuid string) (*Group, error) {
 }
 
 func (fs *customFileSystem) collectionSingleton(id string) (inode, error) {
+       // Return existing singleton, if we have it
        fs.byIDLock.Lock()
-       if n := fs.byID[id]; n != nil {
-               fs.byIDLock.Unlock()
-               return n, nil
-       }
+       existing := fs.byID[id]
        fs.byIDLock.Unlock()
+       if existing != nil {
+               return existing, nil
+       }
 
        coll, err := fs.getCollection(id)
        if err != nil {
@@ -260,12 +264,20 @@ func (fs *customFileSystem) collectionSingleton(id string) (inode, error) {
        cfs := newfs.(*collectionFileSystem)
        cfs.SetParent(fs.byIDRoot, id)
 
+       // Check again in case another goroutine has added a node to
+       // fs.byID since we checked above.
        fs.byIDLock.Lock()
        defer fs.byIDLock.Unlock()
-       if n := fs.byID[id]; n != nil {
-               return n, nil
+       if existing = fs.byID[id]; existing != nil {
+               // Other goroutine won the race. Discard the node we
+               // just made, and return the race winner.
+               return existing, nil
        }
+       // We won the race. Save the new node in fs.byID and
+       // fs.byIDRoot.
        fs.byID[id] = cfs
+       fs.byIDRoot.Lock()
+       defer fs.byIDRoot.Unlock()
        fs.byIDRoot.Child(id, func(inode) (inode, error) { return cfs, nil })
        return cfs, nil
 }
@@ -337,6 +349,18 @@ type hardlink struct {
        name   string
 }
 
+// If the wrapped inode is a filesystem, rootnode returns the wrapped
+// fs's rootnode, otherwise inode itself. This allows
+// (*fileSystem)Rename() to lock the root node of a hardlink-wrapped
+// filesystem.
+func (hl *hardlink) rootnode() inode {
+       if node, ok := hl.inode.(interface{ rootnode() inode }); ok {
+               return node.rootnode()
+       } else {
+               return hl.inode
+       }
+}
+
 func (hl *hardlink) Sync() error {
        if node, ok := hl.inode.(syncer); ok {
                return node.Sync()
@@ -366,3 +390,7 @@ func (hl *hardlink) FileInfo() os.FileInfo {
        }
        return fi
 }
+
+func (hl *hardlink) MemorySize() int64 {
+       return 64 + int64(len(hl.name))
+}