// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: Apache-2.0 package arvados import ( "log" "os" "sync" "time" ) func deferredCollectionFS(fs FileSystem, parent inode, coll Collection) inode { var modTime time.Time if coll.ModifiedAt != nil { modTime = *coll.ModifiedAt } else { modTime = time.Now() } placeholder := &treenode{ fs: fs, parent: parent, inodes: nil, fileinfo: fileinfo{ name: coll.Name, modTime: modTime, mode: 0755 | os.ModeDir, }, } return &deferrednode{wrapped: placeholder, create: func() inode { err := fs.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+coll.UUID, nil, nil) if err != nil { log.Printf("BUG: unhandled error: %s", err) return placeholder } cfs, err := coll.FileSystem(fs, fs) if err != nil { log.Printf("BUG: unhandled error: %s", err) return placeholder } root := cfs.rootnode() root.SetParent(parent, coll.Name) return root }} } // A deferrednode wraps an inode that's expensive to build. Initially, // it responds to basic directory functions by proxying to the given // placeholder. If a caller uses a read/write/lock operation, // deferrednode calls the create() func to create the real inode, and // proxies to the real inode from then on. // // In practice, this means a deferrednode's parent's directory listing // can be generated using only the placeholder, instead of waiting for // create(). type deferrednode struct { wrapped inode create func() inode mtx sync.Mutex created bool } func (dn *deferrednode) realinode() inode { dn.mtx.Lock() defer dn.mtx.Unlock() if !dn.created { dn.wrapped = dn.create() dn.created = true } return dn.wrapped } func (dn *deferrednode) currentinode() inode { dn.mtx.Lock() defer dn.mtx.Unlock() return dn.wrapped } func (dn *deferrednode) Read(p []byte, pos filenodePtr) (int, filenodePtr, error) { return dn.realinode().Read(p, pos) } func (dn *deferrednode) Write(p []byte, pos filenodePtr) (int, filenodePtr, error) { return dn.realinode().Write(p, pos) } func (dn *deferrednode) Child(name string, replace func(inode) (inode, error)) (inode, error) { return dn.realinode().Child(name, replace) } func (dn *deferrednode) Truncate(size int64) error { return dn.realinode().Truncate(size) } func (dn *deferrednode) SetParent(p inode, name string) { dn.realinode().SetParent(p, name) } func (dn *deferrednode) IsDir() bool { return dn.currentinode().IsDir() } func (dn *deferrednode) Readdir() ([]os.FileInfo, error) { return dn.realinode().Readdir() } func (dn *deferrednode) Size() int64 { return dn.currentinode().Size() } func (dn *deferrednode) FileInfo() os.FileInfo { return dn.currentinode().FileInfo() } func (dn *deferrednode) Lock() { dn.realinode().Lock() } func (dn *deferrednode) Unlock() { dn.realinode().Unlock() } func (dn *deferrednode) RLock() { dn.realinode().RLock() } func (dn *deferrednode) RUnlock() { dn.realinode().RUnlock() } func (dn *deferrednode) FS() FileSystem { return dn.currentinode().FS() } func (dn *deferrednode) Parent() inode { return dn.currentinode().Parent() }