20318: Sync cache state after 1% churn instead of 5 minute timer.
[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         "os"
9         "sync"
10 )
11
12 // A deferrednode wraps an inode that's expensive to build. Initially,
13 // it responds to basic directory functions by proxying to the given
14 // placeholder. If a caller uses a read/write/lock operation,
15 // deferrednode calls the create() func to create the real inode, and
16 // proxies to the real inode from then on.
17 //
18 // In practice, this means a deferrednode's parent's directory listing
19 // can be generated using only the placeholder, instead of waiting for
20 // create().
21 type deferrednode struct {
22         wrapped inode
23         create  func() inode
24         mtx     sync.Mutex
25         created bool
26 }
27
28 func (dn *deferrednode) realinode() inode {
29         dn.mtx.Lock()
30         defer dn.mtx.Unlock()
31         if !dn.created {
32                 dn.wrapped = dn.create()
33                 dn.created = true
34         }
35         return dn.wrapped
36 }
37
38 func (dn *deferrednode) currentinode() inode {
39         dn.mtx.Lock()
40         defer dn.mtx.Unlock()
41         return dn.wrapped
42 }
43
44 func (dn *deferrednode) Read(p []byte, pos filenodePtr) (int, filenodePtr, error) {
45         return dn.realinode().Read(p, pos)
46 }
47
48 func (dn *deferrednode) Write(p []byte, pos filenodePtr) (int, filenodePtr, error) {
49         return dn.realinode().Write(p, pos)
50 }
51
52 func (dn *deferrednode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
53         return dn.realinode().Child(name, replace)
54 }
55
56 // Sync is a no-op if the real inode hasn't even been created yet.
57 func (dn *deferrednode) Sync() error {
58         dn.mtx.Lock()
59         defer dn.mtx.Unlock()
60         if !dn.created {
61                 return nil
62         } else if syncer, ok := dn.wrapped.(syncer); ok {
63                 return syncer.Sync()
64         } else {
65                 return ErrInvalidOperation
66         }
67 }
68
69 func (dn *deferrednode) Truncate(size int64) error       { return dn.realinode().Truncate(size) }
70 func (dn *deferrednode) SetParent(p inode, name string)  { dn.realinode().SetParent(p, name) }
71 func (dn *deferrednode) IsDir() bool                     { return dn.currentinode().IsDir() }
72 func (dn *deferrednode) Readdir() ([]os.FileInfo, error) { return dn.realinode().Readdir() }
73 func (dn *deferrednode) Size() int64                     { return dn.currentinode().Size() }
74 func (dn *deferrednode) FileInfo() os.FileInfo           { return dn.currentinode().FileInfo() }
75 func (dn *deferrednode) Lock()                           { dn.realinode().Lock() }
76 func (dn *deferrednode) Unlock()                         { dn.realinode().Unlock() }
77 func (dn *deferrednode) RLock()                          { dn.realinode().RLock() }
78 func (dn *deferrednode) RUnlock()                        { dn.realinode().RUnlock() }
79 func (dn *deferrednode) FS() FileSystem                  { return dn.currentinode().FS() }
80 func (dn *deferrednode) Parent() inode                   { return dn.currentinode().Parent() }
81 func (dn *deferrednode) MemorySize() int64               { return dn.currentinode().MemorySize() }
82 func (dn *deferrednode) Snapshot() (inode, error)        { return dn.realinode().Snapshot() }
83 func (dn *deferrednode) Splice(repl inode) error         { return dn.realinode().Splice(repl) }