Merge branch 'master' into 13804-no-shutdown-wanted-nodes
[arvados.git] / sdk / go / arvados / fs_deferred.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: Apache-2.0
4
5 package arvados
6
7 import (
8         "log"
9         "os"
10         "sync"
11         "time"
12 )
13
14 func deferredCollectionFS(fs FileSystem, parent inode, coll Collection) inode {
15         var modTime time.Time
16         if coll.ModifiedAt != nil {
17                 modTime = *coll.ModifiedAt
18         } else {
19                 modTime = time.Now()
20         }
21         placeholder := &treenode{
22                 fs:     fs,
23                 parent: parent,
24                 inodes: nil,
25                 fileinfo: fileinfo{
26                         name:    coll.Name,
27                         modTime: modTime,
28                         mode:    0755 | os.ModeDir,
29                 },
30         }
31         return &deferrednode{wrapped: placeholder, create: func() inode {
32                 err := fs.RequestAndDecode(&coll, "GET", "arvados/v1/collections/"+coll.UUID, nil, nil)
33                 if err != nil {
34                         log.Printf("BUG: unhandled error: %s", err)
35                         return placeholder
36                 }
37                 cfs, err := coll.FileSystem(fs, fs)
38                 if err != nil {
39                         log.Printf("BUG: unhandled error: %s", err)
40                         return placeholder
41                 }
42                 root := cfs.rootnode()
43                 root.SetParent(parent, coll.Name)
44                 return root
45         }}
46 }
47
48 // A deferrednode wraps an inode that's expensive to build. Initially,
49 // it responds to basic directory functions by proxying to the given
50 // placeholder. If a caller uses a read/write/lock operation,
51 // deferrednode calls the create() func to create the real inode, and
52 // proxies to the real inode from then on.
53 //
54 // In practice, this means a deferrednode's parent's directory listing
55 // can be generated using only the placeholder, instead of waiting for
56 // create().
57 type deferrednode struct {
58         wrapped inode
59         create  func() inode
60         mtx     sync.Mutex
61         created bool
62 }
63
64 func (dn *deferrednode) realinode() inode {
65         dn.mtx.Lock()
66         defer dn.mtx.Unlock()
67         if !dn.created {
68                 dn.wrapped = dn.create()
69                 dn.created = true
70         }
71         return dn.wrapped
72 }
73
74 func (dn *deferrednode) currentinode() inode {
75         dn.mtx.Lock()
76         defer dn.mtx.Unlock()
77         return dn.wrapped
78 }
79
80 func (dn *deferrednode) Read(p []byte, pos filenodePtr) (int, filenodePtr, error) {
81         return dn.realinode().Read(p, pos)
82 }
83
84 func (dn *deferrednode) Write(p []byte, pos filenodePtr) (int, filenodePtr, error) {
85         return dn.realinode().Write(p, pos)
86 }
87
88 func (dn *deferrednode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
89         return dn.realinode().Child(name, replace)
90 }
91
92 func (dn *deferrednode) Truncate(size int64) error       { return dn.realinode().Truncate(size) }
93 func (dn *deferrednode) SetParent(p inode, name string)  { dn.realinode().SetParent(p, name) }
94 func (dn *deferrednode) IsDir() bool                     { return dn.currentinode().IsDir() }
95 func (dn *deferrednode) Readdir() ([]os.FileInfo, error) { return dn.realinode().Readdir() }
96 func (dn *deferrednode) Size() int64                     { return dn.currentinode().Size() }
97 func (dn *deferrednode) FileInfo() os.FileInfo           { return dn.currentinode().FileInfo() }
98 func (dn *deferrednode) Lock()                           { dn.realinode().Lock() }
99 func (dn *deferrednode) Unlock()                         { dn.realinode().Unlock() }
100 func (dn *deferrednode) RLock()                          { dn.realinode().RLock() }
101 func (dn *deferrednode) RUnlock()                        { dn.realinode().RUnlock() }
102 func (dn *deferrednode) FS() FileSystem                  { return dn.currentinode().FS() }
103 func (dn *deferrednode) Parent() inode                   { return dn.currentinode().Parent() }