Merge branch 'master' into 13804-no-shutdown-wanted-nodes
[arvados.git] / sdk / go / arvados / fs_lookup.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         "time"
11 )
12
13 // lookupnode is a caching tree node that is initially empty and calls
14 // loadOne and loadAll to load/update child nodes as needed.
15 //
16 // See (*customFileSystem)MountUsers for example usage.
17 type lookupnode struct {
18         inode
19         loadOne func(parent inode, name string) (inode, error)
20         loadAll func(parent inode) ([]inode, error)
21         stale   func(time.Time) bool
22
23         // internal fields
24         staleLock sync.Mutex
25         staleAll  time.Time
26         staleOne  map[string]time.Time
27 }
28
29 func (ln *lookupnode) Readdir() ([]os.FileInfo, error) {
30         ln.staleLock.Lock()
31         defer ln.staleLock.Unlock()
32         checkTime := time.Now()
33         if ln.stale(ln.staleAll) {
34                 all, err := ln.loadAll(ln)
35                 if err != nil {
36                         return nil, err
37                 }
38                 for _, child := range all {
39                         _, err = ln.inode.Child(child.FileInfo().Name(), func(inode) (inode, error) {
40                                 return child, nil
41                         })
42                         if err != nil {
43                                 return nil, err
44                         }
45                 }
46                 ln.staleAll = checkTime
47                 // No value in ln.staleOne can make a difference to an
48                 // "entry is stale?" test now, because no value is
49                 // newer than ln.staleAll. Reclaim memory.
50                 ln.staleOne = nil
51         }
52         return ln.inode.Readdir()
53 }
54
55 func (ln *lookupnode) Child(name string, replace func(inode) (inode, error)) (inode, error) {
56         ln.staleLock.Lock()
57         defer ln.staleLock.Unlock()
58         checkTime := time.Now()
59         if ln.stale(ln.staleAll) && ln.stale(ln.staleOne[name]) {
60                 _, err := ln.inode.Child(name, func(inode) (inode, error) {
61                         return ln.loadOne(ln, name)
62                 })
63                 if err != nil {
64                         return nil, err
65                 }
66                 if ln.staleOne == nil {
67                         ln.staleOne = map[string]time.Time{name: checkTime}
68                 } else {
69                         ln.staleOne[name] = checkTime
70                 }
71         }
72         return ln.inode.Child(name, replace)
73 }