1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage finder 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "bufio" 19*333d2b36SAndroid Build Coastguard Worker "bytes" 20*333d2b36SAndroid Build Coastguard Worker "encoding/json" 21*333d2b36SAndroid Build Coastguard Worker "errors" 22*333d2b36SAndroid Build Coastguard Worker "fmt" 23*333d2b36SAndroid Build Coastguard Worker "io" 24*333d2b36SAndroid Build Coastguard Worker "os" 25*333d2b36SAndroid Build Coastguard Worker "path/filepath" 26*333d2b36SAndroid Build Coastguard Worker "runtime" 27*333d2b36SAndroid Build Coastguard Worker "sort" 28*333d2b36SAndroid Build Coastguard Worker "strings" 29*333d2b36SAndroid Build Coastguard Worker "sync" 30*333d2b36SAndroid Build Coastguard Worker "sync/atomic" 31*333d2b36SAndroid Build Coastguard Worker "time" 32*333d2b36SAndroid Build Coastguard Worker 33*333d2b36SAndroid Build Coastguard Worker "android/soong/finder/fs" 34*333d2b36SAndroid Build Coastguard Worker) 35*333d2b36SAndroid Build Coastguard Worker 36*333d2b36SAndroid Build Coastguard Worker// This file provides a Finder struct that can quickly search for files satisfying 37*333d2b36SAndroid Build Coastguard Worker// certain criteria. 38*333d2b36SAndroid Build Coastguard Worker// This Finder gets its speed partially from parallelism and partially from caching. 39*333d2b36SAndroid Build Coastguard Worker// If a Stat call returns the same result as last time, then it means Finder 40*333d2b36SAndroid Build Coastguard Worker// can skip the ReadDir call for that dir. 41*333d2b36SAndroid Build Coastguard Worker 42*333d2b36SAndroid Build Coastguard Worker// The primary data structure used by the finder is the field Finder.nodes , 43*333d2b36SAndroid Build Coastguard Worker// which is a tree of nodes of type *pathMap . 44*333d2b36SAndroid Build Coastguard Worker// Each node represents a directory on disk, along with its stats, subdirectories, 45*333d2b36SAndroid Build Coastguard Worker// and contained files. 46*333d2b36SAndroid Build Coastguard Worker 47*333d2b36SAndroid Build Coastguard Worker// The common use case for the Finder is that the caller creates a Finder and gives 48*333d2b36SAndroid Build Coastguard Worker// it the same query that was given to it in the previous execution. 49*333d2b36SAndroid Build Coastguard Worker// In this situation, the major events that take place are: 50*333d2b36SAndroid Build Coastguard Worker// 1. The Finder begins to load its db 51*333d2b36SAndroid Build Coastguard Worker// 2. The Finder begins to stat the directories mentioned in its db (using multiple threads) 52*333d2b36SAndroid Build Coastguard Worker// Calling Stat on each of these directories is generally a large fraction of the total time 53*333d2b36SAndroid Build Coastguard Worker// 3. The Finder begins to construct a separate tree of nodes in each of its threads 54*333d2b36SAndroid Build Coastguard Worker// 4. The Finder merges the individual node trees into the main node tree 55*333d2b36SAndroid Build Coastguard Worker// 5. The Finder may call ReadDir a few times if there are a few directories that are out-of-date 56*333d2b36SAndroid Build Coastguard Worker// These ReadDir calls might prompt additional Stat calls, etc 57*333d2b36SAndroid Build Coastguard Worker// 6. The Finder waits for all loading to complete 58*333d2b36SAndroid Build Coastguard Worker// 7. The Finder searches the cache for files matching the user's query (using multiple threads) 59*333d2b36SAndroid Build Coastguard Worker 60*333d2b36SAndroid Build Coastguard Worker// These are the invariants regarding concurrency: 61*333d2b36SAndroid Build Coastguard Worker// 1. The public methods of Finder are threadsafe. 62*333d2b36SAndroid Build Coastguard Worker// The public methods are only performance-optimized for one caller at a time, however. 63*333d2b36SAndroid Build Coastguard Worker// For the moment, multiple concurrent callers shouldn't expect any better performance than 64*333d2b36SAndroid Build Coastguard Worker// multiple serial callers. 65*333d2b36SAndroid Build Coastguard Worker// 2. While building the node tree, only one thread may ever access the <children> collection of a 66*333d2b36SAndroid Build Coastguard Worker// *pathMap at once. 67*333d2b36SAndroid Build Coastguard Worker// a) The thread that accesses the <children> collection is the thread that discovers the 68*333d2b36SAndroid Build Coastguard Worker// children (by reading them from the cache or by having received a response to ReadDir). 69*333d2b36SAndroid Build Coastguard Worker// 1) Consequently, the thread that discovers the children also spawns requests to stat 70*333d2b36SAndroid Build Coastguard Worker// subdirs. 71*333d2b36SAndroid Build Coastguard Worker// b) Consequently, while building the node tree, no thread may do a lookup of its 72*333d2b36SAndroid Build Coastguard Worker// *pathMap via filepath because another thread may be adding children to the 73*333d2b36SAndroid Build Coastguard Worker// <children> collection of an ancestor node. Additionally, in rare cases, another thread 74*333d2b36SAndroid Build Coastguard Worker// may be removing children from an ancestor node if the children were only discovered to 75*333d2b36SAndroid Build Coastguard Worker// be irrelevant after calling ReadDir (which happens if a prune-file was just added). 76*333d2b36SAndroid Build Coastguard Worker// 3. No query will begin to be serviced until all loading (both reading the db 77*333d2b36SAndroid Build Coastguard Worker// and scanning the filesystem) is complete. 78*333d2b36SAndroid Build Coastguard Worker// Tests indicate that it only takes about 10% as long to search the in-memory cache as to 79*333d2b36SAndroid Build Coastguard Worker// generate it, making this not a huge loss in performance. 80*333d2b36SAndroid Build Coastguard Worker// 4. The parsing of the db and the initial setup of the pathMap tree must complete before 81*333d2b36SAndroid Build Coastguard Worker// beginning to call listDirSync (because listDirSync can create new entries in the pathMap) 82*333d2b36SAndroid Build Coastguard Worker 83*333d2b36SAndroid Build Coastguard Worker// see cmd/finder.go or finder_test.go for usage examples 84*333d2b36SAndroid Build Coastguard Worker 85*333d2b36SAndroid Build Coastguard Worker// Update versionString whenever making a backwards-incompatible change to the cache file format 86*333d2b36SAndroid Build Coastguard Workerconst versionString = "Android finder version 1" 87*333d2b36SAndroid Build Coastguard Worker 88*333d2b36SAndroid Build Coastguard Worker// a CacheParams specifies which files and directories the user wishes be scanned and 89*333d2b36SAndroid Build Coastguard Worker// potentially added to the cache 90*333d2b36SAndroid Build Coastguard Workertype CacheParams struct { 91*333d2b36SAndroid Build Coastguard Worker // WorkingDirectory is used as a base for any relative file paths given to the Finder 92*333d2b36SAndroid Build Coastguard Worker WorkingDirectory string 93*333d2b36SAndroid Build Coastguard Worker 94*333d2b36SAndroid Build Coastguard Worker // RootDirs are the root directories used to initiate the search 95*333d2b36SAndroid Build Coastguard Worker RootDirs []string 96*333d2b36SAndroid Build Coastguard Worker 97*333d2b36SAndroid Build Coastguard Worker // Whether symlinks are followed. If set, symlinks back to their own parent 98*333d2b36SAndroid Build Coastguard Worker // directory don't work. 99*333d2b36SAndroid Build Coastguard Worker FollowSymlinks bool 100*333d2b36SAndroid Build Coastguard Worker 101*333d2b36SAndroid Build Coastguard Worker // ExcludeDirs are directory names that if encountered are removed from the search 102*333d2b36SAndroid Build Coastguard Worker ExcludeDirs []string 103*333d2b36SAndroid Build Coastguard Worker 104*333d2b36SAndroid Build Coastguard Worker // PruneFiles are file names that if encountered prune their entire directory 105*333d2b36SAndroid Build Coastguard Worker // (including siblings) 106*333d2b36SAndroid Build Coastguard Worker PruneFiles []string 107*333d2b36SAndroid Build Coastguard Worker 108*333d2b36SAndroid Build Coastguard Worker // IncludeFiles are file names to include as matches 109*333d2b36SAndroid Build Coastguard Worker IncludeFiles []string 110*333d2b36SAndroid Build Coastguard Worker 111*333d2b36SAndroid Build Coastguard Worker // IncludeSuffixes are filename suffixes to include as matches. 112*333d2b36SAndroid Build Coastguard Worker IncludeSuffixes []string 113*333d2b36SAndroid Build Coastguard Worker} 114*333d2b36SAndroid Build Coastguard Worker 115*333d2b36SAndroid Build Coastguard Worker// a cacheConfig stores the inputs that determine what should be included in the cache 116*333d2b36SAndroid Build Coastguard Workertype cacheConfig struct { 117*333d2b36SAndroid Build Coastguard Worker CacheParams 118*333d2b36SAndroid Build Coastguard Worker 119*333d2b36SAndroid Build Coastguard Worker // FilesystemView is a unique identifier telling which parts of which file systems 120*333d2b36SAndroid Build Coastguard Worker // are readable by the Finder. In practice its value is essentially username@hostname. 121*333d2b36SAndroid Build Coastguard Worker // FilesystemView is set to ensure that a cache file copied to another host or 122*333d2b36SAndroid Build Coastguard Worker // found by another user doesn't inadvertently get reused. 123*333d2b36SAndroid Build Coastguard Worker FilesystemView string 124*333d2b36SAndroid Build Coastguard Worker} 125*333d2b36SAndroid Build Coastguard Worker 126*333d2b36SAndroid Build Coastguard Workerfunc (p *cacheConfig) Dump() ([]byte, error) { 127*333d2b36SAndroid Build Coastguard Worker bytes, err := json.Marshal(p) 128*333d2b36SAndroid Build Coastguard Worker return bytes, err 129*333d2b36SAndroid Build Coastguard Worker} 130*333d2b36SAndroid Build Coastguard Worker 131*333d2b36SAndroid Build Coastguard Worker// a cacheMetadata stores version information about the cache 132*333d2b36SAndroid Build Coastguard Workertype cacheMetadata struct { 133*333d2b36SAndroid Build Coastguard Worker // The Version enables the Finder to determine whether it can even parse the file 134*333d2b36SAndroid Build Coastguard Worker // If the version changes, the entire cache file must be regenerated 135*333d2b36SAndroid Build Coastguard Worker Version string 136*333d2b36SAndroid Build Coastguard Worker 137*333d2b36SAndroid Build Coastguard Worker // The CacheParams enables the Finder to determine whether the parameters match 138*333d2b36SAndroid Build Coastguard Worker // If the CacheParams change, the Finder can choose how much of the cache file to reuse 139*333d2b36SAndroid Build Coastguard Worker // (although in practice, the Finder will probably choose to ignore the entire file anyway) 140*333d2b36SAndroid Build Coastguard Worker Config cacheConfig 141*333d2b36SAndroid Build Coastguard Worker} 142*333d2b36SAndroid Build Coastguard Worker 143*333d2b36SAndroid Build Coastguard Workertype Logger interface { 144*333d2b36SAndroid Build Coastguard Worker Output(calldepth int, s string) error 145*333d2b36SAndroid Build Coastguard Worker} 146*333d2b36SAndroid Build Coastguard Worker 147*333d2b36SAndroid Build Coastguard Worker// the Finder is the main struct that callers will want to use 148*333d2b36SAndroid Build Coastguard Workertype Finder struct { 149*333d2b36SAndroid Build Coastguard Worker // configuration 150*333d2b36SAndroid Build Coastguard Worker DbPath string 151*333d2b36SAndroid Build Coastguard Worker numDbLoadingThreads int 152*333d2b36SAndroid Build Coastguard Worker numSearchingThreads int 153*333d2b36SAndroid Build Coastguard Worker cacheMetadata cacheMetadata 154*333d2b36SAndroid Build Coastguard Worker logger Logger 155*333d2b36SAndroid Build Coastguard Worker filesystem fs.FileSystem 156*333d2b36SAndroid Build Coastguard Worker 157*333d2b36SAndroid Build Coastguard Worker // temporary state 158*333d2b36SAndroid Build Coastguard Worker threadPool *threadPool 159*333d2b36SAndroid Build Coastguard Worker mutex sync.Mutex 160*333d2b36SAndroid Build Coastguard Worker fsErrs []fsErr 161*333d2b36SAndroid Build Coastguard Worker errlock sync.Mutex 162*333d2b36SAndroid Build Coastguard Worker shutdownWaitgroup sync.WaitGroup 163*333d2b36SAndroid Build Coastguard Worker 164*333d2b36SAndroid Build Coastguard Worker // non-temporary state 165*333d2b36SAndroid Build Coastguard Worker modifiedFlag int32 166*333d2b36SAndroid Build Coastguard Worker nodes pathMap 167*333d2b36SAndroid Build Coastguard Worker} 168*333d2b36SAndroid Build Coastguard Worker 169*333d2b36SAndroid Build Coastguard Workervar defaultNumThreads = runtime.NumCPU() * 2 170*333d2b36SAndroid Build Coastguard Worker 171*333d2b36SAndroid Build Coastguard Worker// New creates a new Finder for use 172*333d2b36SAndroid Build Coastguard Workerfunc New(cacheParams CacheParams, filesystem fs.FileSystem, 173*333d2b36SAndroid Build Coastguard Worker logger Logger, dbPath string) (f *Finder, err error) { 174*333d2b36SAndroid Build Coastguard Worker return newImpl(cacheParams, filesystem, logger, dbPath, defaultNumThreads) 175*333d2b36SAndroid Build Coastguard Worker} 176*333d2b36SAndroid Build Coastguard Worker 177*333d2b36SAndroid Build Coastguard Worker// newImpl is like New but accepts more params 178*333d2b36SAndroid Build Coastguard Workerfunc newImpl(cacheParams CacheParams, filesystem fs.FileSystem, 179*333d2b36SAndroid Build Coastguard Worker logger Logger, dbPath string, numThreads int) (f *Finder, err error) { 180*333d2b36SAndroid Build Coastguard Worker numDbLoadingThreads := numThreads 181*333d2b36SAndroid Build Coastguard Worker numSearchingThreads := numThreads 182*333d2b36SAndroid Build Coastguard Worker 183*333d2b36SAndroid Build Coastguard Worker metadata := cacheMetadata{ 184*333d2b36SAndroid Build Coastguard Worker Version: versionString, 185*333d2b36SAndroid Build Coastguard Worker Config: cacheConfig{ 186*333d2b36SAndroid Build Coastguard Worker CacheParams: cacheParams, 187*333d2b36SAndroid Build Coastguard Worker FilesystemView: filesystem.ViewId(), 188*333d2b36SAndroid Build Coastguard Worker }, 189*333d2b36SAndroid Build Coastguard Worker } 190*333d2b36SAndroid Build Coastguard Worker 191*333d2b36SAndroid Build Coastguard Worker f = &Finder{ 192*333d2b36SAndroid Build Coastguard Worker numDbLoadingThreads: numDbLoadingThreads, 193*333d2b36SAndroid Build Coastguard Worker numSearchingThreads: numSearchingThreads, 194*333d2b36SAndroid Build Coastguard Worker cacheMetadata: metadata, 195*333d2b36SAndroid Build Coastguard Worker logger: logger, 196*333d2b36SAndroid Build Coastguard Worker filesystem: filesystem, 197*333d2b36SAndroid Build Coastguard Worker 198*333d2b36SAndroid Build Coastguard Worker nodes: *newPathMap("/"), 199*333d2b36SAndroid Build Coastguard Worker DbPath: dbPath, 200*333d2b36SAndroid Build Coastguard Worker 201*333d2b36SAndroid Build Coastguard Worker shutdownWaitgroup: sync.WaitGroup{}, 202*333d2b36SAndroid Build Coastguard Worker } 203*333d2b36SAndroid Build Coastguard Worker 204*333d2b36SAndroid Build Coastguard Worker f.loadFromFilesystem() 205*333d2b36SAndroid Build Coastguard Worker 206*333d2b36SAndroid Build Coastguard Worker // check for any filesystem errors 207*333d2b36SAndroid Build Coastguard Worker err = f.getErr() 208*333d2b36SAndroid Build Coastguard Worker if err != nil { 209*333d2b36SAndroid Build Coastguard Worker return nil, err 210*333d2b36SAndroid Build Coastguard Worker } 211*333d2b36SAndroid Build Coastguard Worker 212*333d2b36SAndroid Build Coastguard Worker // confirm that every path mentioned in the CacheConfig exists 213*333d2b36SAndroid Build Coastguard Worker for _, path := range cacheParams.RootDirs { 214*333d2b36SAndroid Build Coastguard Worker if !filepath.IsAbs(path) { 215*333d2b36SAndroid Build Coastguard Worker path = filepath.Join(f.cacheMetadata.Config.WorkingDirectory, path) 216*333d2b36SAndroid Build Coastguard Worker } 217*333d2b36SAndroid Build Coastguard Worker node := f.nodes.GetNode(filepath.Clean(path), false) 218*333d2b36SAndroid Build Coastguard Worker if node == nil || node.ModTime == 0 { 219*333d2b36SAndroid Build Coastguard Worker return nil, fmt.Errorf("path %v was specified to be included in the cache but does not exist\n", path) 220*333d2b36SAndroid Build Coastguard Worker } 221*333d2b36SAndroid Build Coastguard Worker } 222*333d2b36SAndroid Build Coastguard Worker 223*333d2b36SAndroid Build Coastguard Worker return f, nil 224*333d2b36SAndroid Build Coastguard Worker} 225*333d2b36SAndroid Build Coastguard Worker 226*333d2b36SAndroid Build Coastguard Worker// FindNamed searches for every cached file 227*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindAll() []string { 228*333d2b36SAndroid Build Coastguard Worker return f.FindAt("/") 229*333d2b36SAndroid Build Coastguard Worker} 230*333d2b36SAndroid Build Coastguard Worker 231*333d2b36SAndroid Build Coastguard Worker// FindNamed searches for every cached file under <rootDir> 232*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindAt(rootDir string) []string { 233*333d2b36SAndroid Build Coastguard Worker filter := func(entries DirEntries) (dirNames []string, fileNames []string) { 234*333d2b36SAndroid Build Coastguard Worker return entries.DirNames, entries.FileNames 235*333d2b36SAndroid Build Coastguard Worker } 236*333d2b36SAndroid Build Coastguard Worker return f.FindMatching(rootDir, filter) 237*333d2b36SAndroid Build Coastguard Worker} 238*333d2b36SAndroid Build Coastguard Worker 239*333d2b36SAndroid Build Coastguard Worker// FindNamed searches for every cached file named <fileName> 240*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindNamed(fileName string) []string { 241*333d2b36SAndroid Build Coastguard Worker return f.FindNamedAt("/", fileName) 242*333d2b36SAndroid Build Coastguard Worker} 243*333d2b36SAndroid Build Coastguard Worker 244*333d2b36SAndroid Build Coastguard Worker// FindNamedAt searches under <rootPath> for every file named <fileName> 245*333d2b36SAndroid Build Coastguard Worker// The reason a caller might use FindNamedAt instead of FindNamed is if they want 246*333d2b36SAndroid Build Coastguard Worker// to limit their search to a subset of the cache 247*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindNamedAt(rootPath string, fileName string) []string { 248*333d2b36SAndroid Build Coastguard Worker filter := func(entries DirEntries) (dirNames []string, fileNames []string) { 249*333d2b36SAndroid Build Coastguard Worker matches := []string{} 250*333d2b36SAndroid Build Coastguard Worker for _, foundName := range entries.FileNames { 251*333d2b36SAndroid Build Coastguard Worker if foundName == fileName { 252*333d2b36SAndroid Build Coastguard Worker matches = append(matches, foundName) 253*333d2b36SAndroid Build Coastguard Worker } 254*333d2b36SAndroid Build Coastguard Worker } 255*333d2b36SAndroid Build Coastguard Worker return entries.DirNames, matches 256*333d2b36SAndroid Build Coastguard Worker 257*333d2b36SAndroid Build Coastguard Worker } 258*333d2b36SAndroid Build Coastguard Worker return f.FindMatching(rootPath, filter) 259*333d2b36SAndroid Build Coastguard Worker} 260*333d2b36SAndroid Build Coastguard Worker 261*333d2b36SAndroid Build Coastguard Worker// FindFirstNamed searches for every file named <fileName> 262*333d2b36SAndroid Build Coastguard Worker// Whenever it finds a match, it stops search subdirectories 263*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindFirstNamed(fileName string) []string { 264*333d2b36SAndroid Build Coastguard Worker return f.FindFirstNamedAt("/", fileName) 265*333d2b36SAndroid Build Coastguard Worker} 266*333d2b36SAndroid Build Coastguard Worker 267*333d2b36SAndroid Build Coastguard Worker// FindFirstNamedAt searches for every file named <fileName> 268*333d2b36SAndroid Build Coastguard Worker// Whenever it finds a match, it stops search subdirectories 269*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindFirstNamedAt(rootPath string, fileName string) []string { 270*333d2b36SAndroid Build Coastguard Worker filter := func(entries DirEntries) (dirNames []string, fileNames []string) { 271*333d2b36SAndroid Build Coastguard Worker matches := []string{} 272*333d2b36SAndroid Build Coastguard Worker for _, foundName := range entries.FileNames { 273*333d2b36SAndroid Build Coastguard Worker if foundName == fileName { 274*333d2b36SAndroid Build Coastguard Worker matches = append(matches, foundName) 275*333d2b36SAndroid Build Coastguard Worker } 276*333d2b36SAndroid Build Coastguard Worker } 277*333d2b36SAndroid Build Coastguard Worker 278*333d2b36SAndroid Build Coastguard Worker if len(matches) > 0 { 279*333d2b36SAndroid Build Coastguard Worker return []string{}, matches 280*333d2b36SAndroid Build Coastguard Worker } 281*333d2b36SAndroid Build Coastguard Worker return entries.DirNames, matches 282*333d2b36SAndroid Build Coastguard Worker } 283*333d2b36SAndroid Build Coastguard Worker return f.FindMatching(rootPath, filter) 284*333d2b36SAndroid Build Coastguard Worker} 285*333d2b36SAndroid Build Coastguard Worker 286*333d2b36SAndroid Build Coastguard Worker// FindMatching is the most general exported function for searching for files in the cache 287*333d2b36SAndroid Build Coastguard Worker// The WalkFunc will be invoked repeatedly and is expected to modify the provided DirEntries 288*333d2b36SAndroid Build Coastguard Worker// in place, removing file paths and directories as desired. 289*333d2b36SAndroid Build Coastguard Worker// WalkFunc will be invoked potentially many times in parallel, and must be threadsafe. 290*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) FindMatching(rootPath string, filter WalkFunc) []string { 291*333d2b36SAndroid Build Coastguard Worker // set up some parameters 292*333d2b36SAndroid Build Coastguard Worker scanStart := time.Now() 293*333d2b36SAndroid Build Coastguard Worker var isRel bool 294*333d2b36SAndroid Build Coastguard Worker workingDir := f.cacheMetadata.Config.WorkingDirectory 295*333d2b36SAndroid Build Coastguard Worker 296*333d2b36SAndroid Build Coastguard Worker isRel = !filepath.IsAbs(rootPath) 297*333d2b36SAndroid Build Coastguard Worker if isRel { 298*333d2b36SAndroid Build Coastguard Worker rootPath = filepath.Join(workingDir, rootPath) 299*333d2b36SAndroid Build Coastguard Worker } 300*333d2b36SAndroid Build Coastguard Worker 301*333d2b36SAndroid Build Coastguard Worker rootPath = filepath.Clean(rootPath) 302*333d2b36SAndroid Build Coastguard Worker 303*333d2b36SAndroid Build Coastguard Worker // ensure nothing else is using the Finder 304*333d2b36SAndroid Build Coastguard Worker f.verbosef("FindMatching waiting for finder to be idle\n") 305*333d2b36SAndroid Build Coastguard Worker f.lock() 306*333d2b36SAndroid Build Coastguard Worker defer f.unlock() 307*333d2b36SAndroid Build Coastguard Worker 308*333d2b36SAndroid Build Coastguard Worker node := f.nodes.GetNode(rootPath, false) 309*333d2b36SAndroid Build Coastguard Worker if node == nil { 310*333d2b36SAndroid Build Coastguard Worker f.verbosef("No data for path %v ; apparently not included in cache params: %v\n", 311*333d2b36SAndroid Build Coastguard Worker rootPath, f.cacheMetadata.Config.CacheParams) 312*333d2b36SAndroid Build Coastguard Worker // path is not found; don't do a search 313*333d2b36SAndroid Build Coastguard Worker return []string{} 314*333d2b36SAndroid Build Coastguard Worker } 315*333d2b36SAndroid Build Coastguard Worker 316*333d2b36SAndroid Build Coastguard Worker // search for matching files 317*333d2b36SAndroid Build Coastguard Worker f.verbosef("Finder finding %v using cache\n", rootPath) 318*333d2b36SAndroid Build Coastguard Worker results := f.findInCacheMultithreaded(node, filter, f.numSearchingThreads) 319*333d2b36SAndroid Build Coastguard Worker 320*333d2b36SAndroid Build Coastguard Worker // format and return results 321*333d2b36SAndroid Build Coastguard Worker if isRel { 322*333d2b36SAndroid Build Coastguard Worker for i := 0; i < len(results); i++ { 323*333d2b36SAndroid Build Coastguard Worker results[i] = strings.Replace(results[i], workingDir+"/", "", 1) 324*333d2b36SAndroid Build Coastguard Worker } 325*333d2b36SAndroid Build Coastguard Worker } 326*333d2b36SAndroid Build Coastguard Worker sort.Strings(results) 327*333d2b36SAndroid Build Coastguard Worker f.verbosef("Found %v files under %v in %v using cache\n", 328*333d2b36SAndroid Build Coastguard Worker len(results), rootPath, time.Since(scanStart)) 329*333d2b36SAndroid Build Coastguard Worker return results 330*333d2b36SAndroid Build Coastguard Worker} 331*333d2b36SAndroid Build Coastguard Worker 332*333d2b36SAndroid Build Coastguard Worker// Shutdown declares that the finder is no longer needed and waits for its cleanup to complete 333*333d2b36SAndroid Build Coastguard Worker// Currently, that only entails waiting for the database dump to complete. 334*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) Shutdown() { 335*333d2b36SAndroid Build Coastguard Worker f.WaitForDbDump() 336*333d2b36SAndroid Build Coastguard Worker} 337*333d2b36SAndroid Build Coastguard Worker 338*333d2b36SAndroid Build Coastguard Worker// WaitForDbDump returns once the database has been written to f.DbPath. 339*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) WaitForDbDump() { 340*333d2b36SAndroid Build Coastguard Worker f.shutdownWaitgroup.Wait() 341*333d2b36SAndroid Build Coastguard Worker} 342*333d2b36SAndroid Build Coastguard Worker 343*333d2b36SAndroid Build Coastguard Worker// End of public api 344*333d2b36SAndroid Build Coastguard Worker 345*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) goDumpDb() { 346*333d2b36SAndroid Build Coastguard Worker if f.wasModified() { 347*333d2b36SAndroid Build Coastguard Worker f.shutdownWaitgroup.Add(1) 348*333d2b36SAndroid Build Coastguard Worker go func() { 349*333d2b36SAndroid Build Coastguard Worker err := f.dumpDb() 350*333d2b36SAndroid Build Coastguard Worker if err != nil { 351*333d2b36SAndroid Build Coastguard Worker f.verbosef("%v\n", err) 352*333d2b36SAndroid Build Coastguard Worker } 353*333d2b36SAndroid Build Coastguard Worker f.shutdownWaitgroup.Done() 354*333d2b36SAndroid Build Coastguard Worker }() 355*333d2b36SAndroid Build Coastguard Worker } else { 356*333d2b36SAndroid Build Coastguard Worker f.verbosef("Skipping dumping unmodified db\n") 357*333d2b36SAndroid Build Coastguard Worker } 358*333d2b36SAndroid Build Coastguard Worker} 359*333d2b36SAndroid Build Coastguard Worker 360*333d2b36SAndroid Build Coastguard Worker// joinCleanPaths is like filepath.Join but is faster because 361*333d2b36SAndroid Build Coastguard Worker// joinCleanPaths doesn't have to support paths ending in "/" or containing ".." 362*333d2b36SAndroid Build Coastguard Workerfunc joinCleanPaths(base string, leaf string) string { 363*333d2b36SAndroid Build Coastguard Worker if base == "" { 364*333d2b36SAndroid Build Coastguard Worker return leaf 365*333d2b36SAndroid Build Coastguard Worker } 366*333d2b36SAndroid Build Coastguard Worker if base == "/" { 367*333d2b36SAndroid Build Coastguard Worker return base + leaf 368*333d2b36SAndroid Build Coastguard Worker } 369*333d2b36SAndroid Build Coastguard Worker if leaf == "" { 370*333d2b36SAndroid Build Coastguard Worker return base 371*333d2b36SAndroid Build Coastguard Worker } 372*333d2b36SAndroid Build Coastguard Worker return base + "/" + leaf 373*333d2b36SAndroid Build Coastguard Worker} 374*333d2b36SAndroid Build Coastguard Worker 375*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) verbosef(format string, args ...interface{}) { 376*333d2b36SAndroid Build Coastguard Worker f.logger.Output(2, fmt.Sprintf(format, args...)) 377*333d2b36SAndroid Build Coastguard Worker} 378*333d2b36SAndroid Build Coastguard Worker 379*333d2b36SAndroid Build Coastguard Worker// loadFromFilesystem populates the in-memory cache based on the contents of the filesystem 380*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) loadFromFilesystem() { 381*333d2b36SAndroid Build Coastguard Worker f.threadPool = newThreadPool(f.numDbLoadingThreads) 382*333d2b36SAndroid Build Coastguard Worker 383*333d2b36SAndroid Build Coastguard Worker err := f.startFromExternalCache() 384*333d2b36SAndroid Build Coastguard Worker if err != nil { 385*333d2b36SAndroid Build Coastguard Worker f.startWithoutExternalCache() 386*333d2b36SAndroid Build Coastguard Worker } 387*333d2b36SAndroid Build Coastguard Worker 388*333d2b36SAndroid Build Coastguard Worker f.goDumpDb() 389*333d2b36SAndroid Build Coastguard Worker 390*333d2b36SAndroid Build Coastguard Worker f.threadPool = nil 391*333d2b36SAndroid Build Coastguard Worker} 392*333d2b36SAndroid Build Coastguard Worker 393*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) startFind(path string) { 394*333d2b36SAndroid Build Coastguard Worker if !filepath.IsAbs(path) { 395*333d2b36SAndroid Build Coastguard Worker path = filepath.Join(f.cacheMetadata.Config.WorkingDirectory, path) 396*333d2b36SAndroid Build Coastguard Worker } 397*333d2b36SAndroid Build Coastguard Worker node := f.nodes.GetNode(path, true) 398*333d2b36SAndroid Build Coastguard Worker f.statDirAsync(node) 399*333d2b36SAndroid Build Coastguard Worker} 400*333d2b36SAndroid Build Coastguard Worker 401*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) lock() { 402*333d2b36SAndroid Build Coastguard Worker f.mutex.Lock() 403*333d2b36SAndroid Build Coastguard Worker} 404*333d2b36SAndroid Build Coastguard Worker 405*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) unlock() { 406*333d2b36SAndroid Build Coastguard Worker f.mutex.Unlock() 407*333d2b36SAndroid Build Coastguard Worker} 408*333d2b36SAndroid Build Coastguard Worker 409*333d2b36SAndroid Build Coastguard Worker// a statResponse is the relevant portion of the response from the filesystem to a Stat call 410*333d2b36SAndroid Build Coastguard Workertype statResponse struct { 411*333d2b36SAndroid Build Coastguard Worker ModTime int64 412*333d2b36SAndroid Build Coastguard Worker Inode uint64 413*333d2b36SAndroid Build Coastguard Worker Device uint64 414*333d2b36SAndroid Build Coastguard Worker} 415*333d2b36SAndroid Build Coastguard Worker 416*333d2b36SAndroid Build Coastguard Worker// a pathAndStats stores a path and its stats 417*333d2b36SAndroid Build Coastguard Workertype pathAndStats struct { 418*333d2b36SAndroid Build Coastguard Worker statResponse 419*333d2b36SAndroid Build Coastguard Worker 420*333d2b36SAndroid Build Coastguard Worker Path string 421*333d2b36SAndroid Build Coastguard Worker} 422*333d2b36SAndroid Build Coastguard Worker 423*333d2b36SAndroid Build Coastguard Worker// a dirFullInfo stores all of the relevant information we know about a directory 424*333d2b36SAndroid Build Coastguard Workertype dirFullInfo struct { 425*333d2b36SAndroid Build Coastguard Worker pathAndStats 426*333d2b36SAndroid Build Coastguard Worker 427*333d2b36SAndroid Build Coastguard Worker FileNames []string 428*333d2b36SAndroid Build Coastguard Worker} 429*333d2b36SAndroid Build Coastguard Worker 430*333d2b36SAndroid Build Coastguard Worker// a PersistedDirInfo is the information about a dir that we save to our cache on disk 431*333d2b36SAndroid Build Coastguard Workertype PersistedDirInfo struct { 432*333d2b36SAndroid Build Coastguard Worker // These field names are short because they are repeated many times in the output json file 433*333d2b36SAndroid Build Coastguard Worker P string // path 434*333d2b36SAndroid Build Coastguard Worker T int64 // modification time 435*333d2b36SAndroid Build Coastguard Worker I uint64 // inode number 436*333d2b36SAndroid Build Coastguard Worker F []string // relevant filenames contained 437*333d2b36SAndroid Build Coastguard Worker} 438*333d2b36SAndroid Build Coastguard Worker 439*333d2b36SAndroid Build Coastguard Worker// a PersistedDirs is the information that we persist for a group of dirs 440*333d2b36SAndroid Build Coastguard Workertype PersistedDirs struct { 441*333d2b36SAndroid Build Coastguard Worker // the device on which each directory is stored 442*333d2b36SAndroid Build Coastguard Worker Device uint64 443*333d2b36SAndroid Build Coastguard Worker // the common root path to which all contained dirs are relative 444*333d2b36SAndroid Build Coastguard Worker Root string 445*333d2b36SAndroid Build Coastguard Worker // the directories themselves 446*333d2b36SAndroid Build Coastguard Worker Dirs []PersistedDirInfo 447*333d2b36SAndroid Build Coastguard Worker} 448*333d2b36SAndroid Build Coastguard Worker 449*333d2b36SAndroid Build Coastguard Worker// a CacheEntry is the smallest unit that can be read and parsed from the cache (on disk) at a time 450*333d2b36SAndroid Build Coastguard Workertype CacheEntry []PersistedDirs 451*333d2b36SAndroid Build Coastguard Worker 452*333d2b36SAndroid Build Coastguard Worker// a DirEntries lists the files and directories contained directly within a specific directory 453*333d2b36SAndroid Build Coastguard Workertype DirEntries struct { 454*333d2b36SAndroid Build Coastguard Worker Path string 455*333d2b36SAndroid Build Coastguard Worker 456*333d2b36SAndroid Build Coastguard Worker // elements of DirNames are just the dir names; they don't include any '/' character 457*333d2b36SAndroid Build Coastguard Worker DirNames []string 458*333d2b36SAndroid Build Coastguard Worker // elements of FileNames are just the file names; they don't include '/' character 459*333d2b36SAndroid Build Coastguard Worker FileNames []string 460*333d2b36SAndroid Build Coastguard Worker} 461*333d2b36SAndroid Build Coastguard Worker 462*333d2b36SAndroid Build Coastguard Worker// a WalkFunc is the type that is passed into various Find functions for determining which 463*333d2b36SAndroid Build Coastguard Worker// directories the caller wishes be walked. The WalkFunc is expected to decide which 464*333d2b36SAndroid Build Coastguard Worker// directories to walk and which files to consider as matches to the original query. 465*333d2b36SAndroid Build Coastguard Workertype WalkFunc func(DirEntries) (dirs []string, files []string) 466*333d2b36SAndroid Build Coastguard Worker 467*333d2b36SAndroid Build Coastguard Worker// a mapNode stores the relevant stats about a directory to be stored in a pathMap 468*333d2b36SAndroid Build Coastguard Workertype mapNode struct { 469*333d2b36SAndroid Build Coastguard Worker statResponse 470*333d2b36SAndroid Build Coastguard Worker FileNames []string 471*333d2b36SAndroid Build Coastguard Worker} 472*333d2b36SAndroid Build Coastguard Worker 473*333d2b36SAndroid Build Coastguard Worker// a pathMap implements the directory tree structure of nodes 474*333d2b36SAndroid Build Coastguard Workertype pathMap struct { 475*333d2b36SAndroid Build Coastguard Worker mapNode 476*333d2b36SAndroid Build Coastguard Worker 477*333d2b36SAndroid Build Coastguard Worker path string 478*333d2b36SAndroid Build Coastguard Worker 479*333d2b36SAndroid Build Coastguard Worker children map[string]*pathMap 480*333d2b36SAndroid Build Coastguard Worker 481*333d2b36SAndroid Build Coastguard Worker // number of descendent nodes, including self 482*333d2b36SAndroid Build Coastguard Worker approximateNumDescendents int 483*333d2b36SAndroid Build Coastguard Worker} 484*333d2b36SAndroid Build Coastguard Worker 485*333d2b36SAndroid Build Coastguard Workerfunc newPathMap(path string) *pathMap { 486*333d2b36SAndroid Build Coastguard Worker result := &pathMap{path: path, children: make(map[string]*pathMap, 4), 487*333d2b36SAndroid Build Coastguard Worker approximateNumDescendents: 1} 488*333d2b36SAndroid Build Coastguard Worker return result 489*333d2b36SAndroid Build Coastguard Worker} 490*333d2b36SAndroid Build Coastguard Worker 491*333d2b36SAndroid Build Coastguard Worker// GetNode returns the node at <path> 492*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) GetNode(path string, createIfNotFound bool) *pathMap { 493*333d2b36SAndroid Build Coastguard Worker if len(path) > 0 && path[0] == '/' { 494*333d2b36SAndroid Build Coastguard Worker path = path[1:] 495*333d2b36SAndroid Build Coastguard Worker } 496*333d2b36SAndroid Build Coastguard Worker 497*333d2b36SAndroid Build Coastguard Worker node := m 498*333d2b36SAndroid Build Coastguard Worker for { 499*333d2b36SAndroid Build Coastguard Worker if path == "" { 500*333d2b36SAndroid Build Coastguard Worker return node 501*333d2b36SAndroid Build Coastguard Worker } 502*333d2b36SAndroid Build Coastguard Worker 503*333d2b36SAndroid Build Coastguard Worker index := strings.Index(path, "/") 504*333d2b36SAndroid Build Coastguard Worker var firstComponent string 505*333d2b36SAndroid Build Coastguard Worker if index >= 0 { 506*333d2b36SAndroid Build Coastguard Worker firstComponent = path[:index] 507*333d2b36SAndroid Build Coastguard Worker path = path[index+1:] 508*333d2b36SAndroid Build Coastguard Worker } else { 509*333d2b36SAndroid Build Coastguard Worker firstComponent = path 510*333d2b36SAndroid Build Coastguard Worker path = "" 511*333d2b36SAndroid Build Coastguard Worker } 512*333d2b36SAndroid Build Coastguard Worker 513*333d2b36SAndroid Build Coastguard Worker child, found := node.children[firstComponent] 514*333d2b36SAndroid Build Coastguard Worker 515*333d2b36SAndroid Build Coastguard Worker if !found { 516*333d2b36SAndroid Build Coastguard Worker if createIfNotFound { 517*333d2b36SAndroid Build Coastguard Worker child = node.newChild(firstComponent) 518*333d2b36SAndroid Build Coastguard Worker } else { 519*333d2b36SAndroid Build Coastguard Worker return nil 520*333d2b36SAndroid Build Coastguard Worker } 521*333d2b36SAndroid Build Coastguard Worker } 522*333d2b36SAndroid Build Coastguard Worker 523*333d2b36SAndroid Build Coastguard Worker node = child 524*333d2b36SAndroid Build Coastguard Worker } 525*333d2b36SAndroid Build Coastguard Worker} 526*333d2b36SAndroid Build Coastguard Worker 527*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) newChild(name string) (child *pathMap) { 528*333d2b36SAndroid Build Coastguard Worker path := joinCleanPaths(m.path, name) 529*333d2b36SAndroid Build Coastguard Worker newChild := newPathMap(path) 530*333d2b36SAndroid Build Coastguard Worker m.children[name] = newChild 531*333d2b36SAndroid Build Coastguard Worker 532*333d2b36SAndroid Build Coastguard Worker return m.children[name] 533*333d2b36SAndroid Build Coastguard Worker} 534*333d2b36SAndroid Build Coastguard Worker 535*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) UpdateNumDescendents() int { 536*333d2b36SAndroid Build Coastguard Worker count := 1 537*333d2b36SAndroid Build Coastguard Worker for _, child := range m.children { 538*333d2b36SAndroid Build Coastguard Worker count += child.approximateNumDescendents 539*333d2b36SAndroid Build Coastguard Worker } 540*333d2b36SAndroid Build Coastguard Worker m.approximateNumDescendents = count 541*333d2b36SAndroid Build Coastguard Worker return count 542*333d2b36SAndroid Build Coastguard Worker} 543*333d2b36SAndroid Build Coastguard Worker 544*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) UpdateNumDescendentsRecursive() { 545*333d2b36SAndroid Build Coastguard Worker for _, child := range m.children { 546*333d2b36SAndroid Build Coastguard Worker child.UpdateNumDescendentsRecursive() 547*333d2b36SAndroid Build Coastguard Worker } 548*333d2b36SAndroid Build Coastguard Worker m.UpdateNumDescendents() 549*333d2b36SAndroid Build Coastguard Worker} 550*333d2b36SAndroid Build Coastguard Worker 551*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) MergeIn(other *pathMap) { 552*333d2b36SAndroid Build Coastguard Worker for key, theirs := range other.children { 553*333d2b36SAndroid Build Coastguard Worker ours, found := m.children[key] 554*333d2b36SAndroid Build Coastguard Worker if found { 555*333d2b36SAndroid Build Coastguard Worker ours.MergeIn(theirs) 556*333d2b36SAndroid Build Coastguard Worker } else { 557*333d2b36SAndroid Build Coastguard Worker m.children[key] = theirs 558*333d2b36SAndroid Build Coastguard Worker } 559*333d2b36SAndroid Build Coastguard Worker } 560*333d2b36SAndroid Build Coastguard Worker if other.ModTime != 0 { 561*333d2b36SAndroid Build Coastguard Worker m.mapNode = other.mapNode 562*333d2b36SAndroid Build Coastguard Worker } 563*333d2b36SAndroid Build Coastguard Worker m.UpdateNumDescendents() 564*333d2b36SAndroid Build Coastguard Worker} 565*333d2b36SAndroid Build Coastguard Worker 566*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) DumpAll() []dirFullInfo { 567*333d2b36SAndroid Build Coastguard Worker results := []dirFullInfo{} 568*333d2b36SAndroid Build Coastguard Worker m.dumpInto("", &results) 569*333d2b36SAndroid Build Coastguard Worker return results 570*333d2b36SAndroid Build Coastguard Worker} 571*333d2b36SAndroid Build Coastguard Worker 572*333d2b36SAndroid Build Coastguard Workerfunc (m *pathMap) dumpInto(path string, results *[]dirFullInfo) { 573*333d2b36SAndroid Build Coastguard Worker *results = append(*results, 574*333d2b36SAndroid Build Coastguard Worker dirFullInfo{ 575*333d2b36SAndroid Build Coastguard Worker pathAndStats{statResponse: m.statResponse, Path: path}, 576*333d2b36SAndroid Build Coastguard Worker m.FileNames}, 577*333d2b36SAndroid Build Coastguard Worker ) 578*333d2b36SAndroid Build Coastguard Worker for key, child := range m.children { 579*333d2b36SAndroid Build Coastguard Worker childPath := joinCleanPaths(path, key) 580*333d2b36SAndroid Build Coastguard Worker if len(childPath) == 0 || childPath[0] != '/' { 581*333d2b36SAndroid Build Coastguard Worker childPath = "/" + childPath 582*333d2b36SAndroid Build Coastguard Worker } 583*333d2b36SAndroid Build Coastguard Worker child.dumpInto(childPath, results) 584*333d2b36SAndroid Build Coastguard Worker } 585*333d2b36SAndroid Build Coastguard Worker} 586*333d2b36SAndroid Build Coastguard Worker 587*333d2b36SAndroid Build Coastguard Worker// a semaphore can be locked by up to <capacity> callers at once 588*333d2b36SAndroid Build Coastguard Workertype semaphore struct { 589*333d2b36SAndroid Build Coastguard Worker pool chan bool 590*333d2b36SAndroid Build Coastguard Worker} 591*333d2b36SAndroid Build Coastguard Worker 592*333d2b36SAndroid Build Coastguard Workerfunc newSemaphore(capacity int) *semaphore { 593*333d2b36SAndroid Build Coastguard Worker return &semaphore{pool: make(chan bool, capacity)} 594*333d2b36SAndroid Build Coastguard Worker} 595*333d2b36SAndroid Build Coastguard Worker 596*333d2b36SAndroid Build Coastguard Workerfunc (l *semaphore) Lock() { 597*333d2b36SAndroid Build Coastguard Worker l.pool <- true 598*333d2b36SAndroid Build Coastguard Worker} 599*333d2b36SAndroid Build Coastguard Worker 600*333d2b36SAndroid Build Coastguard Workerfunc (l *semaphore) Unlock() { 601*333d2b36SAndroid Build Coastguard Worker <-l.pool 602*333d2b36SAndroid Build Coastguard Worker} 603*333d2b36SAndroid Build Coastguard Worker 604*333d2b36SAndroid Build Coastguard Worker// A threadPool runs goroutines and supports throttling and waiting. 605*333d2b36SAndroid Build Coastguard Worker// Without throttling, Go may exhaust the maximum number of various resources, such as 606*333d2b36SAndroid Build Coastguard Worker// threads or file descriptors, and crash the program. 607*333d2b36SAndroid Build Coastguard Workertype threadPool struct { 608*333d2b36SAndroid Build Coastguard Worker receivedRequests sync.WaitGroup 609*333d2b36SAndroid Build Coastguard Worker activeRequests semaphore 610*333d2b36SAndroid Build Coastguard Worker} 611*333d2b36SAndroid Build Coastguard Worker 612*333d2b36SAndroid Build Coastguard Workerfunc newThreadPool(maxNumConcurrentThreads int) *threadPool { 613*333d2b36SAndroid Build Coastguard Worker return &threadPool{ 614*333d2b36SAndroid Build Coastguard Worker receivedRequests: sync.WaitGroup{}, 615*333d2b36SAndroid Build Coastguard Worker activeRequests: *newSemaphore(maxNumConcurrentThreads), 616*333d2b36SAndroid Build Coastguard Worker } 617*333d2b36SAndroid Build Coastguard Worker} 618*333d2b36SAndroid Build Coastguard Worker 619*333d2b36SAndroid Build Coastguard Worker// Run requests to run the given function in its own goroutine 620*333d2b36SAndroid Build Coastguard Workerfunc (p *threadPool) Run(function func()) { 621*333d2b36SAndroid Build Coastguard Worker p.receivedRequests.Add(1) 622*333d2b36SAndroid Build Coastguard Worker // If Run() was called from within a goroutine spawned by this threadPool, 623*333d2b36SAndroid Build Coastguard Worker // then we may need to return from Run() before having capacity to actually 624*333d2b36SAndroid Build Coastguard Worker // run <function>. 625*333d2b36SAndroid Build Coastguard Worker // 626*333d2b36SAndroid Build Coastguard Worker // It's possible that the body of <function> contains a statement (such as a syscall) 627*333d2b36SAndroid Build Coastguard Worker // that will cause Go to pin it to a thread, or will contain a statement that uses 628*333d2b36SAndroid Build Coastguard Worker // another resource that is in short supply (such as a file descriptor), so we can't 629*333d2b36SAndroid Build Coastguard Worker // actually run <function> until we have capacity. 630*333d2b36SAndroid Build Coastguard Worker // 631*333d2b36SAndroid Build Coastguard Worker // However, the semaphore used for synchronization is implemented via a channel and 632*333d2b36SAndroid Build Coastguard Worker // shouldn't require a new thread for each access. 633*333d2b36SAndroid Build Coastguard Worker go func() { 634*333d2b36SAndroid Build Coastguard Worker p.activeRequests.Lock() 635*333d2b36SAndroid Build Coastguard Worker function() 636*333d2b36SAndroid Build Coastguard Worker p.activeRequests.Unlock() 637*333d2b36SAndroid Build Coastguard Worker p.receivedRequests.Done() 638*333d2b36SAndroid Build Coastguard Worker }() 639*333d2b36SAndroid Build Coastguard Worker} 640*333d2b36SAndroid Build Coastguard Worker 641*333d2b36SAndroid Build Coastguard Worker// Wait waits until all goroutines are done, just like sync.WaitGroup's Wait 642*333d2b36SAndroid Build Coastguard Workerfunc (p *threadPool) Wait() { 643*333d2b36SAndroid Build Coastguard Worker p.receivedRequests.Wait() 644*333d2b36SAndroid Build Coastguard Worker} 645*333d2b36SAndroid Build Coastguard Worker 646*333d2b36SAndroid Build Coastguard Workertype fsErr struct { 647*333d2b36SAndroid Build Coastguard Worker path string 648*333d2b36SAndroid Build Coastguard Worker err error 649*333d2b36SAndroid Build Coastguard Worker} 650*333d2b36SAndroid Build Coastguard Worker 651*333d2b36SAndroid Build Coastguard Workerfunc (e fsErr) String() string { 652*333d2b36SAndroid Build Coastguard Worker return e.path + ": " + e.err.Error() 653*333d2b36SAndroid Build Coastguard Worker} 654*333d2b36SAndroid Build Coastguard Worker 655*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) serializeCacheEntry(dirInfos []dirFullInfo) ([]byte, error) { 656*333d2b36SAndroid Build Coastguard Worker // group each dirFullInfo by its Device, to avoid having to repeat it in the output 657*333d2b36SAndroid Build Coastguard Worker dirsByDevice := map[uint64][]PersistedDirInfo{} 658*333d2b36SAndroid Build Coastguard Worker for _, entry := range dirInfos { 659*333d2b36SAndroid Build Coastguard Worker _, found := dirsByDevice[entry.Device] 660*333d2b36SAndroid Build Coastguard Worker if !found { 661*333d2b36SAndroid Build Coastguard Worker dirsByDevice[entry.Device] = []PersistedDirInfo{} 662*333d2b36SAndroid Build Coastguard Worker } 663*333d2b36SAndroid Build Coastguard Worker dirsByDevice[entry.Device] = append(dirsByDevice[entry.Device], 664*333d2b36SAndroid Build Coastguard Worker PersistedDirInfo{P: entry.Path, T: entry.ModTime, I: entry.Inode, F: entry.FileNames}) 665*333d2b36SAndroid Build Coastguard Worker } 666*333d2b36SAndroid Build Coastguard Worker 667*333d2b36SAndroid Build Coastguard Worker cacheEntry := CacheEntry{} 668*333d2b36SAndroid Build Coastguard Worker 669*333d2b36SAndroid Build Coastguard Worker for device, infos := range dirsByDevice { 670*333d2b36SAndroid Build Coastguard Worker // find common prefix 671*333d2b36SAndroid Build Coastguard Worker prefix := "" 672*333d2b36SAndroid Build Coastguard Worker if len(infos) > 0 { 673*333d2b36SAndroid Build Coastguard Worker prefix = infos[0].P 674*333d2b36SAndroid Build Coastguard Worker } 675*333d2b36SAndroid Build Coastguard Worker for _, info := range infos { 676*333d2b36SAndroid Build Coastguard Worker for !strings.HasPrefix(info.P+"/", prefix+"/") { 677*333d2b36SAndroid Build Coastguard Worker prefix = filepath.Dir(prefix) 678*333d2b36SAndroid Build Coastguard Worker if prefix == "/" { 679*333d2b36SAndroid Build Coastguard Worker break 680*333d2b36SAndroid Build Coastguard Worker } 681*333d2b36SAndroid Build Coastguard Worker } 682*333d2b36SAndroid Build Coastguard Worker } 683*333d2b36SAndroid Build Coastguard Worker // remove common prefix 684*333d2b36SAndroid Build Coastguard Worker for i := range infos { 685*333d2b36SAndroid Build Coastguard Worker suffix := strings.Replace(infos[i].P, prefix, "", 1) 686*333d2b36SAndroid Build Coastguard Worker if len(suffix) > 0 && suffix[0] == '/' { 687*333d2b36SAndroid Build Coastguard Worker suffix = suffix[1:] 688*333d2b36SAndroid Build Coastguard Worker } 689*333d2b36SAndroid Build Coastguard Worker infos[i].P = suffix 690*333d2b36SAndroid Build Coastguard Worker } 691*333d2b36SAndroid Build Coastguard Worker 692*333d2b36SAndroid Build Coastguard Worker // turn the map (keyed by device) into a list of structs with labeled fields 693*333d2b36SAndroid Build Coastguard Worker // this is to improve readability of the output 694*333d2b36SAndroid Build Coastguard Worker cacheEntry = append(cacheEntry, PersistedDirs{Device: device, Root: prefix, Dirs: infos}) 695*333d2b36SAndroid Build Coastguard Worker } 696*333d2b36SAndroid Build Coastguard Worker 697*333d2b36SAndroid Build Coastguard Worker // convert to json. 698*333d2b36SAndroid Build Coastguard Worker // it would save some space to use a different format than json for the db file, 699*333d2b36SAndroid Build Coastguard Worker // but the space and time savings are small, and json is easy for humans to read 700*333d2b36SAndroid Build Coastguard Worker bytes, err := json.Marshal(cacheEntry) 701*333d2b36SAndroid Build Coastguard Worker return bytes, err 702*333d2b36SAndroid Build Coastguard Worker} 703*333d2b36SAndroid Build Coastguard Worker 704*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) parseCacheEntry(bytes []byte) ([]dirFullInfo, error) { 705*333d2b36SAndroid Build Coastguard Worker var cacheEntry CacheEntry 706*333d2b36SAndroid Build Coastguard Worker err := json.Unmarshal(bytes, &cacheEntry) 707*333d2b36SAndroid Build Coastguard Worker if err != nil { 708*333d2b36SAndroid Build Coastguard Worker return nil, err 709*333d2b36SAndroid Build Coastguard Worker } 710*333d2b36SAndroid Build Coastguard Worker 711*333d2b36SAndroid Build Coastguard Worker // convert from a CacheEntry to a []dirFullInfo (by copying a few fields) 712*333d2b36SAndroid Build Coastguard Worker capacity := 0 713*333d2b36SAndroid Build Coastguard Worker for _, element := range cacheEntry { 714*333d2b36SAndroid Build Coastguard Worker capacity += len(element.Dirs) 715*333d2b36SAndroid Build Coastguard Worker } 716*333d2b36SAndroid Build Coastguard Worker nodes := make([]dirFullInfo, capacity) 717*333d2b36SAndroid Build Coastguard Worker count := 0 718*333d2b36SAndroid Build Coastguard Worker for _, element := range cacheEntry { 719*333d2b36SAndroid Build Coastguard Worker for _, dir := range element.Dirs { 720*333d2b36SAndroid Build Coastguard Worker path := joinCleanPaths(element.Root, dir.P) 721*333d2b36SAndroid Build Coastguard Worker 722*333d2b36SAndroid Build Coastguard Worker nodes[count] = dirFullInfo{ 723*333d2b36SAndroid Build Coastguard Worker pathAndStats: pathAndStats{ 724*333d2b36SAndroid Build Coastguard Worker statResponse: statResponse{ 725*333d2b36SAndroid Build Coastguard Worker ModTime: dir.T, Inode: dir.I, Device: element.Device, 726*333d2b36SAndroid Build Coastguard Worker }, 727*333d2b36SAndroid Build Coastguard Worker Path: path}, 728*333d2b36SAndroid Build Coastguard Worker FileNames: dir.F} 729*333d2b36SAndroid Build Coastguard Worker count++ 730*333d2b36SAndroid Build Coastguard Worker } 731*333d2b36SAndroid Build Coastguard Worker } 732*333d2b36SAndroid Build Coastguard Worker return nodes, nil 733*333d2b36SAndroid Build Coastguard Worker} 734*333d2b36SAndroid Build Coastguard Worker 735*333d2b36SAndroid Build Coastguard Worker// We use the following separator byte to distinguish individually parseable blocks of json 736*333d2b36SAndroid Build Coastguard Worker// because we know this separator won't appear in the json that we're parsing. 737*333d2b36SAndroid Build Coastguard Worker// 738*333d2b36SAndroid Build Coastguard Worker// The newline byte can only appear in a UTF-8 stream if the newline character appears, because: 739*333d2b36SAndroid Build Coastguard Worker// - The newline character is encoded as "0000 1010" in binary ("0a" in hex) 740*333d2b36SAndroid Build Coastguard Worker// - UTF-8 dictates that bytes beginning with a "0" bit are never emitted as part of a multibyte 741*333d2b36SAndroid Build Coastguard Worker// character. 742*333d2b36SAndroid Build Coastguard Worker// 743*333d2b36SAndroid Build Coastguard Worker// We know that the newline character will never appear in our json string, because: 744*333d2b36SAndroid Build Coastguard Worker// - If a newline character appears as part of a data string, then json encoding will 745*333d2b36SAndroid Build Coastguard Worker// emit two characters instead: '\' and 'n'. 746*333d2b36SAndroid Build Coastguard Worker// - The json encoder that we use doesn't emit the optional newlines between any of its 747*333d2b36SAndroid Build Coastguard Worker// other outputs. 748*333d2b36SAndroid Build Coastguard Workerconst lineSeparator = byte('\n') 749*333d2b36SAndroid Build Coastguard Worker 750*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) readLine(reader *bufio.Reader) ([]byte, error) { 751*333d2b36SAndroid Build Coastguard Worker return reader.ReadBytes(lineSeparator) 752*333d2b36SAndroid Build Coastguard Worker} 753*333d2b36SAndroid Build Coastguard Worker 754*333d2b36SAndroid Build Coastguard Worker// validateCacheHeader reads the cache header from cacheReader and tells whether the cache is compatible with this Finder 755*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) validateCacheHeader(cacheReader *bufio.Reader) bool { 756*333d2b36SAndroid Build Coastguard Worker cacheVersionBytes, err := f.readLine(cacheReader) 757*333d2b36SAndroid Build Coastguard Worker if err != nil { 758*333d2b36SAndroid Build Coastguard Worker f.verbosef("Failed to read database header; database is invalid\n") 759*333d2b36SAndroid Build Coastguard Worker return false 760*333d2b36SAndroid Build Coastguard Worker } 761*333d2b36SAndroid Build Coastguard Worker if len(cacheVersionBytes) > 0 && cacheVersionBytes[len(cacheVersionBytes)-1] == lineSeparator { 762*333d2b36SAndroid Build Coastguard Worker cacheVersionBytes = cacheVersionBytes[:len(cacheVersionBytes)-1] 763*333d2b36SAndroid Build Coastguard Worker } 764*333d2b36SAndroid Build Coastguard Worker cacheVersionString := string(cacheVersionBytes) 765*333d2b36SAndroid Build Coastguard Worker currentVersion := f.cacheMetadata.Version 766*333d2b36SAndroid Build Coastguard Worker if cacheVersionString != currentVersion { 767*333d2b36SAndroid Build Coastguard Worker f.verbosef("Version changed from %q to %q, database is not applicable\n", cacheVersionString, currentVersion) 768*333d2b36SAndroid Build Coastguard Worker return false 769*333d2b36SAndroid Build Coastguard Worker } 770*333d2b36SAndroid Build Coastguard Worker 771*333d2b36SAndroid Build Coastguard Worker cacheParamBytes, err := f.readLine(cacheReader) 772*333d2b36SAndroid Build Coastguard Worker if err != nil { 773*333d2b36SAndroid Build Coastguard Worker f.verbosef("Failed to read database search params; database is invalid\n") 774*333d2b36SAndroid Build Coastguard Worker return false 775*333d2b36SAndroid Build Coastguard Worker } 776*333d2b36SAndroid Build Coastguard Worker 777*333d2b36SAndroid Build Coastguard Worker if len(cacheParamBytes) > 0 && cacheParamBytes[len(cacheParamBytes)-1] == lineSeparator { 778*333d2b36SAndroid Build Coastguard Worker cacheParamBytes = cacheParamBytes[:len(cacheParamBytes)-1] 779*333d2b36SAndroid Build Coastguard Worker } 780*333d2b36SAndroid Build Coastguard Worker 781*333d2b36SAndroid Build Coastguard Worker currentParamBytes, err := f.cacheMetadata.Config.Dump() 782*333d2b36SAndroid Build Coastguard Worker if err != nil { 783*333d2b36SAndroid Build Coastguard Worker panic("Finder failed to serialize its parameters") 784*333d2b36SAndroid Build Coastguard Worker } 785*333d2b36SAndroid Build Coastguard Worker cacheParamString := string(cacheParamBytes) 786*333d2b36SAndroid Build Coastguard Worker currentParamString := string(currentParamBytes) 787*333d2b36SAndroid Build Coastguard Worker if cacheParamString != currentParamString { 788*333d2b36SAndroid Build Coastguard Worker f.verbosef("Params changed from %q to %q, database is not applicable\n", cacheParamString, currentParamString) 789*333d2b36SAndroid Build Coastguard Worker return false 790*333d2b36SAndroid Build Coastguard Worker } 791*333d2b36SAndroid Build Coastguard Worker return true 792*333d2b36SAndroid Build Coastguard Worker} 793*333d2b36SAndroid Build Coastguard Worker 794*333d2b36SAndroid Build Coastguard Worker// loadBytes compares the cache info in <data> to the state of the filesystem 795*333d2b36SAndroid Build Coastguard Worker// loadBytes returns a map representing <data> and also a slice of dirs that need to be re-walked 796*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) loadBytes(id int, data []byte) (m *pathMap, dirsToWalk []string, err error) { 797*333d2b36SAndroid Build Coastguard Worker 798*333d2b36SAndroid Build Coastguard Worker helperStartTime := time.Now() 799*333d2b36SAndroid Build Coastguard Worker 800*333d2b36SAndroid Build Coastguard Worker cachedNodes, err := f.parseCacheEntry(data) 801*333d2b36SAndroid Build Coastguard Worker if err != nil { 802*333d2b36SAndroid Build Coastguard Worker return nil, nil, fmt.Errorf("Failed to parse block %v: %v\n", id, err.Error()) 803*333d2b36SAndroid Build Coastguard Worker } 804*333d2b36SAndroid Build Coastguard Worker 805*333d2b36SAndroid Build Coastguard Worker unmarshalDate := time.Now() 806*333d2b36SAndroid Build Coastguard Worker f.verbosef("Unmarshaled %v objects for %v in %v\n", 807*333d2b36SAndroid Build Coastguard Worker len(cachedNodes), id, unmarshalDate.Sub(helperStartTime)) 808*333d2b36SAndroid Build Coastguard Worker 809*333d2b36SAndroid Build Coastguard Worker tempMap := newPathMap("/") 810*333d2b36SAndroid Build Coastguard Worker stats := make([]statResponse, len(cachedNodes)) 811*333d2b36SAndroid Build Coastguard Worker 812*333d2b36SAndroid Build Coastguard Worker for i, node := range cachedNodes { 813*333d2b36SAndroid Build Coastguard Worker // check the file system for an updated timestamp 814*333d2b36SAndroid Build Coastguard Worker stats[i] = f.statDirSync(node.Path) 815*333d2b36SAndroid Build Coastguard Worker } 816*333d2b36SAndroid Build Coastguard Worker 817*333d2b36SAndroid Build Coastguard Worker dirsToWalk = []string{} 818*333d2b36SAndroid Build Coastguard Worker for i, cachedNode := range cachedNodes { 819*333d2b36SAndroid Build Coastguard Worker updated := stats[i] 820*333d2b36SAndroid Build Coastguard Worker // save the cached value 821*333d2b36SAndroid Build Coastguard Worker container := tempMap.GetNode(cachedNode.Path, true) 822*333d2b36SAndroid Build Coastguard Worker container.mapNode = mapNode{statResponse: updated} 823*333d2b36SAndroid Build Coastguard Worker 824*333d2b36SAndroid Build Coastguard Worker // if the metadata changed and the directory still exists, then 825*333d2b36SAndroid Build Coastguard Worker // make a note to walk it later 826*333d2b36SAndroid Build Coastguard Worker if !f.isInfoUpToDate(cachedNode.statResponse, updated) && updated.ModTime != 0 { 827*333d2b36SAndroid Build Coastguard Worker f.setModified() 828*333d2b36SAndroid Build Coastguard Worker // make a note that the directory needs to be walked 829*333d2b36SAndroid Build Coastguard Worker dirsToWalk = append(dirsToWalk, cachedNode.Path) 830*333d2b36SAndroid Build Coastguard Worker } else { 831*333d2b36SAndroid Build Coastguard Worker container.mapNode.FileNames = cachedNode.FileNames 832*333d2b36SAndroid Build Coastguard Worker } 833*333d2b36SAndroid Build Coastguard Worker } 834*333d2b36SAndroid Build Coastguard Worker // count the number of nodes to improve our understanding of the shape of the tree, 835*333d2b36SAndroid Build Coastguard Worker // thereby improving parallelism of subsequent searches 836*333d2b36SAndroid Build Coastguard Worker tempMap.UpdateNumDescendentsRecursive() 837*333d2b36SAndroid Build Coastguard Worker 838*333d2b36SAndroid Build Coastguard Worker f.verbosef("Statted inodes of block %v in %v\n", id, time.Now().Sub(unmarshalDate)) 839*333d2b36SAndroid Build Coastguard Worker return tempMap, dirsToWalk, nil 840*333d2b36SAndroid Build Coastguard Worker} 841*333d2b36SAndroid Build Coastguard Worker 842*333d2b36SAndroid Build Coastguard Worker// startFromExternalCache loads the cache database from disk 843*333d2b36SAndroid Build Coastguard Worker// startFromExternalCache waits to return until the load of the cache db is complete, but 844*333d2b36SAndroid Build Coastguard Worker// startFromExternalCache does not wait for all every listDir() or statDir() request to complete 845*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) startFromExternalCache() (err error) { 846*333d2b36SAndroid Build Coastguard Worker startTime := time.Now() 847*333d2b36SAndroid Build Coastguard Worker dbPath := f.DbPath 848*333d2b36SAndroid Build Coastguard Worker 849*333d2b36SAndroid Build Coastguard Worker // open cache file and validate its header 850*333d2b36SAndroid Build Coastguard Worker reader, err := f.filesystem.Open(dbPath) 851*333d2b36SAndroid Build Coastguard Worker if err != nil { 852*333d2b36SAndroid Build Coastguard Worker return errors.New("No data to load from database\n") 853*333d2b36SAndroid Build Coastguard Worker } 854*333d2b36SAndroid Build Coastguard Worker defer reader.Close() 855*333d2b36SAndroid Build Coastguard Worker bufferedReader := bufio.NewReader(reader) 856*333d2b36SAndroid Build Coastguard Worker if !f.validateCacheHeader(bufferedReader) { 857*333d2b36SAndroid Build Coastguard Worker return errors.New("Cache header does not match") 858*333d2b36SAndroid Build Coastguard Worker } 859*333d2b36SAndroid Build Coastguard Worker f.verbosef("Database header matches, will attempt to use database %v\n", f.DbPath) 860*333d2b36SAndroid Build Coastguard Worker 861*333d2b36SAndroid Build Coastguard Worker // read the file and spawn threads to process it 862*333d2b36SAndroid Build Coastguard Worker nodesToWalk := [][]*pathMap{} 863*333d2b36SAndroid Build Coastguard Worker mainTree := newPathMap("/") 864*333d2b36SAndroid Build Coastguard Worker 865*333d2b36SAndroid Build Coastguard Worker // read the blocks and stream them into <blockChannel> 866*333d2b36SAndroid Build Coastguard Worker type dataBlock struct { 867*333d2b36SAndroid Build Coastguard Worker id int 868*333d2b36SAndroid Build Coastguard Worker err error 869*333d2b36SAndroid Build Coastguard Worker data []byte 870*333d2b36SAndroid Build Coastguard Worker } 871*333d2b36SAndroid Build Coastguard Worker blockChannel := make(chan dataBlock, f.numDbLoadingThreads) 872*333d2b36SAndroid Build Coastguard Worker readBlocks := func() { 873*333d2b36SAndroid Build Coastguard Worker index := 0 874*333d2b36SAndroid Build Coastguard Worker for { 875*333d2b36SAndroid Build Coastguard Worker // It takes some time to unmarshal the input from json, so we want 876*333d2b36SAndroid Build Coastguard Worker // to unmarshal it in parallel. In order to find valid places to 877*333d2b36SAndroid Build Coastguard Worker // break the input, we scan for the line separators that we inserted 878*333d2b36SAndroid Build Coastguard Worker // (for this purpose) when we dumped the database. 879*333d2b36SAndroid Build Coastguard Worker data, err := f.readLine(bufferedReader) 880*333d2b36SAndroid Build Coastguard Worker var response dataBlock 881*333d2b36SAndroid Build Coastguard Worker done := false 882*333d2b36SAndroid Build Coastguard Worker if err != nil && err != io.EOF { 883*333d2b36SAndroid Build Coastguard Worker response = dataBlock{id: index, err: err, data: nil} 884*333d2b36SAndroid Build Coastguard Worker done = true 885*333d2b36SAndroid Build Coastguard Worker } else { 886*333d2b36SAndroid Build Coastguard Worker done = (err == io.EOF) 887*333d2b36SAndroid Build Coastguard Worker response = dataBlock{id: index, err: nil, data: data} 888*333d2b36SAndroid Build Coastguard Worker } 889*333d2b36SAndroid Build Coastguard Worker blockChannel <- response 890*333d2b36SAndroid Build Coastguard Worker index++ 891*333d2b36SAndroid Build Coastguard Worker duration := time.Since(startTime) 892*333d2b36SAndroid Build Coastguard Worker f.verbosef("Read block %v after %v\n", index, duration) 893*333d2b36SAndroid Build Coastguard Worker if done { 894*333d2b36SAndroid Build Coastguard Worker f.verbosef("Read %v blocks in %v\n", index, duration) 895*333d2b36SAndroid Build Coastguard Worker close(blockChannel) 896*333d2b36SAndroid Build Coastguard Worker return 897*333d2b36SAndroid Build Coastguard Worker } 898*333d2b36SAndroid Build Coastguard Worker } 899*333d2b36SAndroid Build Coastguard Worker } 900*333d2b36SAndroid Build Coastguard Worker go readBlocks() 901*333d2b36SAndroid Build Coastguard Worker 902*333d2b36SAndroid Build Coastguard Worker // Read from <blockChannel> and stream the responses into <resultChannel>. 903*333d2b36SAndroid Build Coastguard Worker type workResponse struct { 904*333d2b36SAndroid Build Coastguard Worker id int 905*333d2b36SAndroid Build Coastguard Worker err error 906*333d2b36SAndroid Build Coastguard Worker tree *pathMap 907*333d2b36SAndroid Build Coastguard Worker updatedDirs []string 908*333d2b36SAndroid Build Coastguard Worker } 909*333d2b36SAndroid Build Coastguard Worker resultChannel := make(chan workResponse) 910*333d2b36SAndroid Build Coastguard Worker processBlocks := func() { 911*333d2b36SAndroid Build Coastguard Worker numProcessed := 0 912*333d2b36SAndroid Build Coastguard Worker threadPool := newThreadPool(f.numDbLoadingThreads) 913*333d2b36SAndroid Build Coastguard Worker for { 914*333d2b36SAndroid Build Coastguard Worker // get a block to process 915*333d2b36SAndroid Build Coastguard Worker block, received := <-blockChannel 916*333d2b36SAndroid Build Coastguard Worker if !received { 917*333d2b36SAndroid Build Coastguard Worker break 918*333d2b36SAndroid Build Coastguard Worker } 919*333d2b36SAndroid Build Coastguard Worker 920*333d2b36SAndroid Build Coastguard Worker if block.err != nil { 921*333d2b36SAndroid Build Coastguard Worker resultChannel <- workResponse{err: block.err} 922*333d2b36SAndroid Build Coastguard Worker break 923*333d2b36SAndroid Build Coastguard Worker } 924*333d2b36SAndroid Build Coastguard Worker numProcessed++ 925*333d2b36SAndroid Build Coastguard Worker // wait until there is CPU available to process it 926*333d2b36SAndroid Build Coastguard Worker threadPool.Run( 927*333d2b36SAndroid Build Coastguard Worker func() { 928*333d2b36SAndroid Build Coastguard Worker processStartTime := time.Now() 929*333d2b36SAndroid Build Coastguard Worker f.verbosef("Starting to process block %v after %v\n", 930*333d2b36SAndroid Build Coastguard Worker block.id, processStartTime.Sub(startTime)) 931*333d2b36SAndroid Build Coastguard Worker tempMap, updatedDirs, err := f.loadBytes(block.id, block.data) 932*333d2b36SAndroid Build Coastguard Worker var response workResponse 933*333d2b36SAndroid Build Coastguard Worker if err != nil { 934*333d2b36SAndroid Build Coastguard Worker f.verbosef( 935*333d2b36SAndroid Build Coastguard Worker "Block %v failed to parse with error %v\n", 936*333d2b36SAndroid Build Coastguard Worker block.id, err) 937*333d2b36SAndroid Build Coastguard Worker response = workResponse{err: err} 938*333d2b36SAndroid Build Coastguard Worker } else { 939*333d2b36SAndroid Build Coastguard Worker response = workResponse{ 940*333d2b36SAndroid Build Coastguard Worker id: block.id, 941*333d2b36SAndroid Build Coastguard Worker err: nil, 942*333d2b36SAndroid Build Coastguard Worker tree: tempMap, 943*333d2b36SAndroid Build Coastguard Worker updatedDirs: updatedDirs, 944*333d2b36SAndroid Build Coastguard Worker } 945*333d2b36SAndroid Build Coastguard Worker } 946*333d2b36SAndroid Build Coastguard Worker f.verbosef("Processed block %v in %v\n", 947*333d2b36SAndroid Build Coastguard Worker block.id, time.Since(processStartTime), 948*333d2b36SAndroid Build Coastguard Worker ) 949*333d2b36SAndroid Build Coastguard Worker resultChannel <- response 950*333d2b36SAndroid Build Coastguard Worker }, 951*333d2b36SAndroid Build Coastguard Worker ) 952*333d2b36SAndroid Build Coastguard Worker } 953*333d2b36SAndroid Build Coastguard Worker threadPool.Wait() 954*333d2b36SAndroid Build Coastguard Worker f.verbosef("Finished processing %v blocks in %v\n", 955*333d2b36SAndroid Build Coastguard Worker numProcessed, time.Since(startTime)) 956*333d2b36SAndroid Build Coastguard Worker close(resultChannel) 957*333d2b36SAndroid Build Coastguard Worker } 958*333d2b36SAndroid Build Coastguard Worker go processBlocks() 959*333d2b36SAndroid Build Coastguard Worker 960*333d2b36SAndroid Build Coastguard Worker // Read from <resultChannel> and use the results 961*333d2b36SAndroid Build Coastguard Worker combineResults := func() (err error) { 962*333d2b36SAndroid Build Coastguard Worker for { 963*333d2b36SAndroid Build Coastguard Worker result, received := <-resultChannel 964*333d2b36SAndroid Build Coastguard Worker if !received { 965*333d2b36SAndroid Build Coastguard Worker break 966*333d2b36SAndroid Build Coastguard Worker } 967*333d2b36SAndroid Build Coastguard Worker if err != nil { 968*333d2b36SAndroid Build Coastguard Worker // In case of an error, wait for work to complete before 969*333d2b36SAndroid Build Coastguard Worker // returning the error. This ensures that any subsequent 970*333d2b36SAndroid Build Coastguard Worker // work doesn't need to compete for resources (and possibly 971*333d2b36SAndroid Build Coastguard Worker // fail due to, for example, a filesystem limit on the number of 972*333d2b36SAndroid Build Coastguard Worker // concurrently open files) with past work. 973*333d2b36SAndroid Build Coastguard Worker continue 974*333d2b36SAndroid Build Coastguard Worker } 975*333d2b36SAndroid Build Coastguard Worker if result.err != nil { 976*333d2b36SAndroid Build Coastguard Worker err = result.err 977*333d2b36SAndroid Build Coastguard Worker continue 978*333d2b36SAndroid Build Coastguard Worker } 979*333d2b36SAndroid Build Coastguard Worker // update main tree 980*333d2b36SAndroid Build Coastguard Worker mainTree.MergeIn(result.tree) 981*333d2b36SAndroid Build Coastguard Worker // record any new directories that we will need to Stat() 982*333d2b36SAndroid Build Coastguard Worker updatedNodes := make([]*pathMap, len(result.updatedDirs)) 983*333d2b36SAndroid Build Coastguard Worker for j, dir := range result.updatedDirs { 984*333d2b36SAndroid Build Coastguard Worker node := mainTree.GetNode(dir, false) 985*333d2b36SAndroid Build Coastguard Worker updatedNodes[j] = node 986*333d2b36SAndroid Build Coastguard Worker } 987*333d2b36SAndroid Build Coastguard Worker nodesToWalk = append(nodesToWalk, updatedNodes) 988*333d2b36SAndroid Build Coastguard Worker } 989*333d2b36SAndroid Build Coastguard Worker return err 990*333d2b36SAndroid Build Coastguard Worker } 991*333d2b36SAndroid Build Coastguard Worker err = combineResults() 992*333d2b36SAndroid Build Coastguard Worker if err != nil { 993*333d2b36SAndroid Build Coastguard Worker return err 994*333d2b36SAndroid Build Coastguard Worker } 995*333d2b36SAndroid Build Coastguard Worker 996*333d2b36SAndroid Build Coastguard Worker f.nodes = *mainTree 997*333d2b36SAndroid Build Coastguard Worker 998*333d2b36SAndroid Build Coastguard Worker // after having loaded the entire db and therefore created entries for 999*333d2b36SAndroid Build Coastguard Worker // the directories we know of, now it's safe to start calling ReadDir on 1000*333d2b36SAndroid Build Coastguard Worker // any updated directories 1001*333d2b36SAndroid Build Coastguard Worker for i := range nodesToWalk { 1002*333d2b36SAndroid Build Coastguard Worker f.listDirsAsync(nodesToWalk[i]) 1003*333d2b36SAndroid Build Coastguard Worker } 1004*333d2b36SAndroid Build Coastguard Worker f.verbosef("Loaded db and statted known dirs in %v\n", time.Since(startTime)) 1005*333d2b36SAndroid Build Coastguard Worker f.threadPool.Wait() 1006*333d2b36SAndroid Build Coastguard Worker f.verbosef("Loaded db and statted all dirs in %v\n", time.Now().Sub(startTime)) 1007*333d2b36SAndroid Build Coastguard Worker 1008*333d2b36SAndroid Build Coastguard Worker return err 1009*333d2b36SAndroid Build Coastguard Worker} 1010*333d2b36SAndroid Build Coastguard Worker 1011*333d2b36SAndroid Build Coastguard Worker// startWithoutExternalCache starts scanning the filesystem according to the cache config 1012*333d2b36SAndroid Build Coastguard Worker// startWithoutExternalCache should be called if startFromExternalCache is not applicable 1013*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) startWithoutExternalCache() { 1014*333d2b36SAndroid Build Coastguard Worker startTime := time.Now() 1015*333d2b36SAndroid Build Coastguard Worker configDirs := f.cacheMetadata.Config.RootDirs 1016*333d2b36SAndroid Build Coastguard Worker 1017*333d2b36SAndroid Build Coastguard Worker // clean paths 1018*333d2b36SAndroid Build Coastguard Worker candidates := make([]string, len(configDirs)) 1019*333d2b36SAndroid Build Coastguard Worker for i, dir := range configDirs { 1020*333d2b36SAndroid Build Coastguard Worker candidates[i] = filepath.Clean(dir) 1021*333d2b36SAndroid Build Coastguard Worker } 1022*333d2b36SAndroid Build Coastguard Worker // remove duplicates 1023*333d2b36SAndroid Build Coastguard Worker dirsToScan := make([]string, 0, len(configDirs)) 1024*333d2b36SAndroid Build Coastguard Worker for _, candidate := range candidates { 1025*333d2b36SAndroid Build Coastguard Worker include := true 1026*333d2b36SAndroid Build Coastguard Worker for _, included := range dirsToScan { 1027*333d2b36SAndroid Build Coastguard Worker if included == "/" || strings.HasPrefix(candidate+"/", included+"/") { 1028*333d2b36SAndroid Build Coastguard Worker include = false 1029*333d2b36SAndroid Build Coastguard Worker break 1030*333d2b36SAndroid Build Coastguard Worker } 1031*333d2b36SAndroid Build Coastguard Worker } 1032*333d2b36SAndroid Build Coastguard Worker if include { 1033*333d2b36SAndroid Build Coastguard Worker dirsToScan = append(dirsToScan, candidate) 1034*333d2b36SAndroid Build Coastguard Worker } 1035*333d2b36SAndroid Build Coastguard Worker } 1036*333d2b36SAndroid Build Coastguard Worker 1037*333d2b36SAndroid Build Coastguard Worker // start searching finally 1038*333d2b36SAndroid Build Coastguard Worker for _, path := range dirsToScan { 1039*333d2b36SAndroid Build Coastguard Worker f.verbosef("Starting find of %v\n", path) 1040*333d2b36SAndroid Build Coastguard Worker f.startFind(path) 1041*333d2b36SAndroid Build Coastguard Worker } 1042*333d2b36SAndroid Build Coastguard Worker 1043*333d2b36SAndroid Build Coastguard Worker f.threadPool.Wait() 1044*333d2b36SAndroid Build Coastguard Worker 1045*333d2b36SAndroid Build Coastguard Worker f.verbosef("Scanned filesystem (not using cache) in %v\n", time.Now().Sub(startTime)) 1046*333d2b36SAndroid Build Coastguard Worker} 1047*333d2b36SAndroid Build Coastguard Worker 1048*333d2b36SAndroid Build Coastguard Worker// isInfoUpToDate tells whether <new> can confirm that results computed at <old> are still valid 1049*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) isInfoUpToDate(old statResponse, new statResponse) (equal bool) { 1050*333d2b36SAndroid Build Coastguard Worker if old.Inode != new.Inode { 1051*333d2b36SAndroid Build Coastguard Worker return false 1052*333d2b36SAndroid Build Coastguard Worker } 1053*333d2b36SAndroid Build Coastguard Worker if old.ModTime != new.ModTime { 1054*333d2b36SAndroid Build Coastguard Worker return false 1055*333d2b36SAndroid Build Coastguard Worker } 1056*333d2b36SAndroid Build Coastguard Worker if old.Device != new.Device { 1057*333d2b36SAndroid Build Coastguard Worker return false 1058*333d2b36SAndroid Build Coastguard Worker } 1059*333d2b36SAndroid Build Coastguard Worker return true 1060*333d2b36SAndroid Build Coastguard Worker} 1061*333d2b36SAndroid Build Coastguard Worker 1062*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) wasModified() bool { 1063*333d2b36SAndroid Build Coastguard Worker return atomic.LoadInt32(&f.modifiedFlag) > 0 1064*333d2b36SAndroid Build Coastguard Worker} 1065*333d2b36SAndroid Build Coastguard Worker 1066*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) setModified() { 1067*333d2b36SAndroid Build Coastguard Worker var newVal int32 1068*333d2b36SAndroid Build Coastguard Worker newVal = 1 1069*333d2b36SAndroid Build Coastguard Worker atomic.StoreInt32(&f.modifiedFlag, newVal) 1070*333d2b36SAndroid Build Coastguard Worker} 1071*333d2b36SAndroid Build Coastguard Worker 1072*333d2b36SAndroid Build Coastguard Worker// sortedDirEntries exports directory entries to facilitate dumping them to the external cache 1073*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) sortedDirEntries() []dirFullInfo { 1074*333d2b36SAndroid Build Coastguard Worker startTime := time.Now() 1075*333d2b36SAndroid Build Coastguard Worker nodes := make([]dirFullInfo, 0) 1076*333d2b36SAndroid Build Coastguard Worker for _, node := range f.nodes.DumpAll() { 1077*333d2b36SAndroid Build Coastguard Worker if node.ModTime != 0 { 1078*333d2b36SAndroid Build Coastguard Worker nodes = append(nodes, node) 1079*333d2b36SAndroid Build Coastguard Worker } 1080*333d2b36SAndroid Build Coastguard Worker } 1081*333d2b36SAndroid Build Coastguard Worker discoveryDate := time.Now() 1082*333d2b36SAndroid Build Coastguard Worker f.verbosef("Generated %v cache entries in %v\n", len(nodes), discoveryDate.Sub(startTime)) 1083*333d2b36SAndroid Build Coastguard Worker less := func(i int, j int) bool { 1084*333d2b36SAndroid Build Coastguard Worker return nodes[i].Path < nodes[j].Path 1085*333d2b36SAndroid Build Coastguard Worker } 1086*333d2b36SAndroid Build Coastguard Worker sort.Slice(nodes, less) 1087*333d2b36SAndroid Build Coastguard Worker sortDate := time.Now() 1088*333d2b36SAndroid Build Coastguard Worker f.verbosef("Sorted %v cache entries in %v\n", len(nodes), sortDate.Sub(discoveryDate)) 1089*333d2b36SAndroid Build Coastguard Worker 1090*333d2b36SAndroid Build Coastguard Worker return nodes 1091*333d2b36SAndroid Build Coastguard Worker} 1092*333d2b36SAndroid Build Coastguard Worker 1093*333d2b36SAndroid Build Coastguard Worker// serializeDb converts the cache database into a form to save to disk 1094*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) serializeDb() ([]byte, error) { 1095*333d2b36SAndroid Build Coastguard Worker // sort dir entries 1096*333d2b36SAndroid Build Coastguard Worker var entryList = f.sortedDirEntries() 1097*333d2b36SAndroid Build Coastguard Worker 1098*333d2b36SAndroid Build Coastguard Worker // Generate an output file that can be conveniently loaded using the same number of threads 1099*333d2b36SAndroid Build Coastguard Worker // as were used in this execution (because presumably that will be the number of threads 1100*333d2b36SAndroid Build Coastguard Worker // used in the next execution too) 1101*333d2b36SAndroid Build Coastguard Worker 1102*333d2b36SAndroid Build Coastguard Worker // generate header 1103*333d2b36SAndroid Build Coastguard Worker header := []byte{} 1104*333d2b36SAndroid Build Coastguard Worker header = append(header, []byte(f.cacheMetadata.Version)...) 1105*333d2b36SAndroid Build Coastguard Worker header = append(header, lineSeparator) 1106*333d2b36SAndroid Build Coastguard Worker configDump, err := f.cacheMetadata.Config.Dump() 1107*333d2b36SAndroid Build Coastguard Worker if err != nil { 1108*333d2b36SAndroid Build Coastguard Worker return nil, err 1109*333d2b36SAndroid Build Coastguard Worker } 1110*333d2b36SAndroid Build Coastguard Worker header = append(header, configDump...) 1111*333d2b36SAndroid Build Coastguard Worker 1112*333d2b36SAndroid Build Coastguard Worker // serialize individual blocks in parallel 1113*333d2b36SAndroid Build Coastguard Worker numBlocks := f.numDbLoadingThreads 1114*333d2b36SAndroid Build Coastguard Worker if numBlocks > len(entryList) { 1115*333d2b36SAndroid Build Coastguard Worker numBlocks = len(entryList) 1116*333d2b36SAndroid Build Coastguard Worker } 1117*333d2b36SAndroid Build Coastguard Worker blocks := make([][]byte, 1+numBlocks) 1118*333d2b36SAndroid Build Coastguard Worker blocks[0] = header 1119*333d2b36SAndroid Build Coastguard Worker blockMin := 0 1120*333d2b36SAndroid Build Coastguard Worker wg := sync.WaitGroup{} 1121*333d2b36SAndroid Build Coastguard Worker var errLock sync.Mutex 1122*333d2b36SAndroid Build Coastguard Worker 1123*333d2b36SAndroid Build Coastguard Worker for i := 1; i <= numBlocks; i++ { 1124*333d2b36SAndroid Build Coastguard Worker // identify next block 1125*333d2b36SAndroid Build Coastguard Worker blockMax := len(entryList) * i / numBlocks 1126*333d2b36SAndroid Build Coastguard Worker block := entryList[blockMin:blockMax] 1127*333d2b36SAndroid Build Coastguard Worker 1128*333d2b36SAndroid Build Coastguard Worker // process block 1129*333d2b36SAndroid Build Coastguard Worker wg.Add(1) 1130*333d2b36SAndroid Build Coastguard Worker go func(index int, block []dirFullInfo) { 1131*333d2b36SAndroid Build Coastguard Worker byteBlock, subErr := f.serializeCacheEntry(block) 1132*333d2b36SAndroid Build Coastguard Worker f.verbosef("Serialized block %v into %v bytes\n", index, len(byteBlock)) 1133*333d2b36SAndroid Build Coastguard Worker if subErr != nil { 1134*333d2b36SAndroid Build Coastguard Worker f.verbosef("%v\n", subErr.Error()) 1135*333d2b36SAndroid Build Coastguard Worker errLock.Lock() 1136*333d2b36SAndroid Build Coastguard Worker err = subErr 1137*333d2b36SAndroid Build Coastguard Worker errLock.Unlock() 1138*333d2b36SAndroid Build Coastguard Worker } else { 1139*333d2b36SAndroid Build Coastguard Worker blocks[index] = byteBlock 1140*333d2b36SAndroid Build Coastguard Worker } 1141*333d2b36SAndroid Build Coastguard Worker wg.Done() 1142*333d2b36SAndroid Build Coastguard Worker }(i, block) 1143*333d2b36SAndroid Build Coastguard Worker 1144*333d2b36SAndroid Build Coastguard Worker blockMin = blockMax 1145*333d2b36SAndroid Build Coastguard Worker } 1146*333d2b36SAndroid Build Coastguard Worker 1147*333d2b36SAndroid Build Coastguard Worker wg.Wait() 1148*333d2b36SAndroid Build Coastguard Worker 1149*333d2b36SAndroid Build Coastguard Worker if err != nil { 1150*333d2b36SAndroid Build Coastguard Worker return nil, err 1151*333d2b36SAndroid Build Coastguard Worker } 1152*333d2b36SAndroid Build Coastguard Worker 1153*333d2b36SAndroid Build Coastguard Worker content := bytes.Join(blocks, []byte{lineSeparator}) 1154*333d2b36SAndroid Build Coastguard Worker 1155*333d2b36SAndroid Build Coastguard Worker return content, nil 1156*333d2b36SAndroid Build Coastguard Worker} 1157*333d2b36SAndroid Build Coastguard Worker 1158*333d2b36SAndroid Build Coastguard Worker// dumpDb saves the cache database to disk 1159*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) dumpDb() error { 1160*333d2b36SAndroid Build Coastguard Worker startTime := time.Now() 1161*333d2b36SAndroid Build Coastguard Worker f.verbosef("Dumping db\n") 1162*333d2b36SAndroid Build Coastguard Worker 1163*333d2b36SAndroid Build Coastguard Worker tempPath := f.DbPath + ".tmp" 1164*333d2b36SAndroid Build Coastguard Worker 1165*333d2b36SAndroid Build Coastguard Worker bytes, err := f.serializeDb() 1166*333d2b36SAndroid Build Coastguard Worker if err != nil { 1167*333d2b36SAndroid Build Coastguard Worker return err 1168*333d2b36SAndroid Build Coastguard Worker } 1169*333d2b36SAndroid Build Coastguard Worker serializeDate := time.Now() 1170*333d2b36SAndroid Build Coastguard Worker f.verbosef("Serialized db in %v\n", serializeDate.Sub(startTime)) 1171*333d2b36SAndroid Build Coastguard Worker // dump file and atomically move 1172*333d2b36SAndroid Build Coastguard Worker err = f.filesystem.WriteFile(tempPath, bytes, 0777) 1173*333d2b36SAndroid Build Coastguard Worker if err != nil { 1174*333d2b36SAndroid Build Coastguard Worker return err 1175*333d2b36SAndroid Build Coastguard Worker } 1176*333d2b36SAndroid Build Coastguard Worker err = f.filesystem.Rename(tempPath, f.DbPath) 1177*333d2b36SAndroid Build Coastguard Worker if err != nil { 1178*333d2b36SAndroid Build Coastguard Worker return err 1179*333d2b36SAndroid Build Coastguard Worker } 1180*333d2b36SAndroid Build Coastguard Worker 1181*333d2b36SAndroid Build Coastguard Worker f.verbosef("Wrote db in %v\n", time.Now().Sub(serializeDate)) 1182*333d2b36SAndroid Build Coastguard Worker return nil 1183*333d2b36SAndroid Build Coastguard Worker 1184*333d2b36SAndroid Build Coastguard Worker} 1185*333d2b36SAndroid Build Coastguard Worker 1186*333d2b36SAndroid Build Coastguard Worker// canIgnoreFsErr checks for certain classes of filesystem errors that are safe to ignore 1187*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) canIgnoreFsErr(err error) bool { 1188*333d2b36SAndroid Build Coastguard Worker pathErr, isPathErr := err.(*os.PathError) 1189*333d2b36SAndroid Build Coastguard Worker if !isPathErr { 1190*333d2b36SAndroid Build Coastguard Worker // Don't recognize this error 1191*333d2b36SAndroid Build Coastguard Worker return false 1192*333d2b36SAndroid Build Coastguard Worker } 1193*333d2b36SAndroid Build Coastguard Worker if os.IsPermission(pathErr) { 1194*333d2b36SAndroid Build Coastguard Worker // Permission errors are ignored: 1195*333d2b36SAndroid Build Coastguard Worker // https://issuetracker.google.com/37553659 1196*333d2b36SAndroid Build Coastguard Worker // https://github.com/google/kati/pull/116 1197*333d2b36SAndroid Build Coastguard Worker return true 1198*333d2b36SAndroid Build Coastguard Worker } 1199*333d2b36SAndroid Build Coastguard Worker if pathErr.Err == os.ErrNotExist { 1200*333d2b36SAndroid Build Coastguard Worker // If a directory doesn't exist, that generally means the cache is out-of-date 1201*333d2b36SAndroid Build Coastguard Worker return true 1202*333d2b36SAndroid Build Coastguard Worker } 1203*333d2b36SAndroid Build Coastguard Worker // Don't recognize this error 1204*333d2b36SAndroid Build Coastguard Worker return false 1205*333d2b36SAndroid Build Coastguard Worker} 1206*333d2b36SAndroid Build Coastguard Worker 1207*333d2b36SAndroid Build Coastguard Worker// onFsError should be called whenever a potentially fatal error is returned from a filesystem call 1208*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) onFsError(path string, err error) { 1209*333d2b36SAndroid Build Coastguard Worker if !f.canIgnoreFsErr(err) { 1210*333d2b36SAndroid Build Coastguard Worker // We could send the errors through a channel instead, although that would cause this call 1211*333d2b36SAndroid Build Coastguard Worker // to block unless we preallocated a sufficient buffer or spawned a reader thread. 1212*333d2b36SAndroid Build Coastguard Worker // Although it wouldn't be too complicated to spawn a reader thread, it's still slightly 1213*333d2b36SAndroid Build Coastguard Worker // more convenient to use a lock. Only in an unusual situation should this code be 1214*333d2b36SAndroid Build Coastguard Worker // invoked anyway. 1215*333d2b36SAndroid Build Coastguard Worker f.errlock.Lock() 1216*333d2b36SAndroid Build Coastguard Worker f.fsErrs = append(f.fsErrs, fsErr{path: path, err: err}) 1217*333d2b36SAndroid Build Coastguard Worker f.errlock.Unlock() 1218*333d2b36SAndroid Build Coastguard Worker } 1219*333d2b36SAndroid Build Coastguard Worker} 1220*333d2b36SAndroid Build Coastguard Worker 1221*333d2b36SAndroid Build Coastguard Worker// discardErrsForPrunedPaths removes any errors for paths that are no longer included in the cache 1222*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) discardErrsForPrunedPaths() { 1223*333d2b36SAndroid Build Coastguard Worker // This function could be somewhat inefficient due to being single-threaded, 1224*333d2b36SAndroid Build Coastguard Worker // but the length of f.fsErrs should be approximately 0, so it shouldn't take long anyway. 1225*333d2b36SAndroid Build Coastguard Worker relevantErrs := make([]fsErr, 0, len(f.fsErrs)) 1226*333d2b36SAndroid Build Coastguard Worker for _, fsErr := range f.fsErrs { 1227*333d2b36SAndroid Build Coastguard Worker path := fsErr.path 1228*333d2b36SAndroid Build Coastguard Worker node := f.nodes.GetNode(path, false) 1229*333d2b36SAndroid Build Coastguard Worker if node != nil { 1230*333d2b36SAndroid Build Coastguard Worker // The path in question wasn't pruned due to a failure to process a parent directory. 1231*333d2b36SAndroid Build Coastguard Worker // So, the failure to process this path is important 1232*333d2b36SAndroid Build Coastguard Worker relevantErrs = append(relevantErrs, fsErr) 1233*333d2b36SAndroid Build Coastguard Worker } 1234*333d2b36SAndroid Build Coastguard Worker } 1235*333d2b36SAndroid Build Coastguard Worker f.fsErrs = relevantErrs 1236*333d2b36SAndroid Build Coastguard Worker} 1237*333d2b36SAndroid Build Coastguard Worker 1238*333d2b36SAndroid Build Coastguard Worker// getErr returns an error based on previous calls to onFsErr, if any 1239*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) getErr() error { 1240*333d2b36SAndroid Build Coastguard Worker f.discardErrsForPrunedPaths() 1241*333d2b36SAndroid Build Coastguard Worker 1242*333d2b36SAndroid Build Coastguard Worker numErrs := len(f.fsErrs) 1243*333d2b36SAndroid Build Coastguard Worker if numErrs < 1 { 1244*333d2b36SAndroid Build Coastguard Worker return nil 1245*333d2b36SAndroid Build Coastguard Worker } 1246*333d2b36SAndroid Build Coastguard Worker 1247*333d2b36SAndroid Build Coastguard Worker maxNumErrsToInclude := 10 1248*333d2b36SAndroid Build Coastguard Worker message := "" 1249*333d2b36SAndroid Build Coastguard Worker if numErrs > maxNumErrsToInclude { 1250*333d2b36SAndroid Build Coastguard Worker message = fmt.Sprintf("finder encountered %v errors: %v...", numErrs, f.fsErrs[:maxNumErrsToInclude]) 1251*333d2b36SAndroid Build Coastguard Worker } else { 1252*333d2b36SAndroid Build Coastguard Worker message = fmt.Sprintf("finder encountered %v errors: %v", numErrs, f.fsErrs) 1253*333d2b36SAndroid Build Coastguard Worker } 1254*333d2b36SAndroid Build Coastguard Worker 1255*333d2b36SAndroid Build Coastguard Worker return errors.New(message) 1256*333d2b36SAndroid Build Coastguard Worker} 1257*333d2b36SAndroid Build Coastguard Worker 1258*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) statDirAsync(dir *pathMap) { 1259*333d2b36SAndroid Build Coastguard Worker node := dir 1260*333d2b36SAndroid Build Coastguard Worker path := dir.path 1261*333d2b36SAndroid Build Coastguard Worker f.threadPool.Run( 1262*333d2b36SAndroid Build Coastguard Worker func() { 1263*333d2b36SAndroid Build Coastguard Worker updatedStats := f.statDirSync(path) 1264*333d2b36SAndroid Build Coastguard Worker 1265*333d2b36SAndroid Build Coastguard Worker if !f.isInfoUpToDate(node.statResponse, updatedStats) { 1266*333d2b36SAndroid Build Coastguard Worker node.mapNode = mapNode{ 1267*333d2b36SAndroid Build Coastguard Worker statResponse: updatedStats, 1268*333d2b36SAndroid Build Coastguard Worker FileNames: []string{}, 1269*333d2b36SAndroid Build Coastguard Worker } 1270*333d2b36SAndroid Build Coastguard Worker f.setModified() 1271*333d2b36SAndroid Build Coastguard Worker if node.statResponse.ModTime != 0 { 1272*333d2b36SAndroid Build Coastguard Worker // modification time was updated, so re-scan for 1273*333d2b36SAndroid Build Coastguard Worker // child directories 1274*333d2b36SAndroid Build Coastguard Worker f.listDirAsync(dir) 1275*333d2b36SAndroid Build Coastguard Worker } 1276*333d2b36SAndroid Build Coastguard Worker } 1277*333d2b36SAndroid Build Coastguard Worker }, 1278*333d2b36SAndroid Build Coastguard Worker ) 1279*333d2b36SAndroid Build Coastguard Worker} 1280*333d2b36SAndroid Build Coastguard Worker 1281*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) statDirSync(path string) statResponse { 1282*333d2b36SAndroid Build Coastguard Worker 1283*333d2b36SAndroid Build Coastguard Worker fileInfo, err := f.filesystem.Lstat(path) 1284*333d2b36SAndroid Build Coastguard Worker 1285*333d2b36SAndroid Build Coastguard Worker var stats statResponse 1286*333d2b36SAndroid Build Coastguard Worker if err != nil { 1287*333d2b36SAndroid Build Coastguard Worker // possibly record this error 1288*333d2b36SAndroid Build Coastguard Worker f.onFsError(path, err) 1289*333d2b36SAndroid Build Coastguard Worker // in case of a failure to stat the directory, treat the directory as missing (modTime = 0) 1290*333d2b36SAndroid Build Coastguard Worker return stats 1291*333d2b36SAndroid Build Coastguard Worker } 1292*333d2b36SAndroid Build Coastguard Worker modTime := fileInfo.ModTime() 1293*333d2b36SAndroid Build Coastguard Worker stats = statResponse{} 1294*333d2b36SAndroid Build Coastguard Worker inode, err := f.filesystem.InodeNumber(fileInfo) 1295*333d2b36SAndroid Build Coastguard Worker if err != nil { 1296*333d2b36SAndroid Build Coastguard Worker panic(fmt.Sprintf("Could not get inode number of %v: %v\n", path, err.Error())) 1297*333d2b36SAndroid Build Coastguard Worker } 1298*333d2b36SAndroid Build Coastguard Worker stats.Inode = inode 1299*333d2b36SAndroid Build Coastguard Worker device, err := f.filesystem.DeviceNumber(fileInfo) 1300*333d2b36SAndroid Build Coastguard Worker if err != nil { 1301*333d2b36SAndroid Build Coastguard Worker panic(fmt.Sprintf("Could not get device number of %v: %v\n", path, err.Error())) 1302*333d2b36SAndroid Build Coastguard Worker } 1303*333d2b36SAndroid Build Coastguard Worker stats.Device = device 1304*333d2b36SAndroid Build Coastguard Worker permissionsChangeTime, err := f.filesystem.PermTime(fileInfo) 1305*333d2b36SAndroid Build Coastguard Worker 1306*333d2b36SAndroid Build Coastguard Worker if err != nil { 1307*333d2b36SAndroid Build Coastguard Worker panic(fmt.Sprintf("Could not get permissions modification time (CTime) of %v: %v\n", path, err.Error())) 1308*333d2b36SAndroid Build Coastguard Worker } 1309*333d2b36SAndroid Build Coastguard Worker // We're only interested in knowing whether anything about the directory 1310*333d2b36SAndroid Build Coastguard Worker // has changed since last check, so we use the latest of the two 1311*333d2b36SAndroid Build Coastguard Worker // modification times (content modification (mtime) and 1312*333d2b36SAndroid Build Coastguard Worker // permission modification (ctime)) 1313*333d2b36SAndroid Build Coastguard Worker if permissionsChangeTime.After(modTime) { 1314*333d2b36SAndroid Build Coastguard Worker modTime = permissionsChangeTime 1315*333d2b36SAndroid Build Coastguard Worker } 1316*333d2b36SAndroid Build Coastguard Worker stats.ModTime = modTime.UnixNano() 1317*333d2b36SAndroid Build Coastguard Worker 1318*333d2b36SAndroid Build Coastguard Worker return stats 1319*333d2b36SAndroid Build Coastguard Worker} 1320*333d2b36SAndroid Build Coastguard Worker 1321*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) shouldIncludeFile(fileName string) bool { 1322*333d2b36SAndroid Build Coastguard Worker for _, includedName := range f.cacheMetadata.Config.IncludeFiles { 1323*333d2b36SAndroid Build Coastguard Worker if fileName == includedName { 1324*333d2b36SAndroid Build Coastguard Worker return true 1325*333d2b36SAndroid Build Coastguard Worker } 1326*333d2b36SAndroid Build Coastguard Worker } 1327*333d2b36SAndroid Build Coastguard Worker for _, includeSuffix := range f.cacheMetadata.Config.IncludeSuffixes { 1328*333d2b36SAndroid Build Coastguard Worker if strings.HasSuffix(fileName, includeSuffix) { 1329*333d2b36SAndroid Build Coastguard Worker return true 1330*333d2b36SAndroid Build Coastguard Worker } 1331*333d2b36SAndroid Build Coastguard Worker } 1332*333d2b36SAndroid Build Coastguard Worker return false 1333*333d2b36SAndroid Build Coastguard Worker} 1334*333d2b36SAndroid Build Coastguard Worker 1335*333d2b36SAndroid Build Coastguard Worker// pruneCacheCandidates removes the items that we don't want to include in our persistent cache 1336*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) pruneCacheCandidates(items *DirEntries) { 1337*333d2b36SAndroid Build Coastguard Worker 1338*333d2b36SAndroid Build Coastguard Worker for _, fileName := range items.FileNames { 1339*333d2b36SAndroid Build Coastguard Worker for _, abortedName := range f.cacheMetadata.Config.PruneFiles { 1340*333d2b36SAndroid Build Coastguard Worker if fileName == abortedName { 1341*333d2b36SAndroid Build Coastguard Worker items.FileNames = []string{} 1342*333d2b36SAndroid Build Coastguard Worker items.DirNames = []string{} 1343*333d2b36SAndroid Build Coastguard Worker return 1344*333d2b36SAndroid Build Coastguard Worker } 1345*333d2b36SAndroid Build Coastguard Worker } 1346*333d2b36SAndroid Build Coastguard Worker } 1347*333d2b36SAndroid Build Coastguard Worker 1348*333d2b36SAndroid Build Coastguard Worker // remove any files that aren't the ones we want to include 1349*333d2b36SAndroid Build Coastguard Worker writeIndex := 0 1350*333d2b36SAndroid Build Coastguard Worker for _, fileName := range items.FileNames { 1351*333d2b36SAndroid Build Coastguard Worker if f.shouldIncludeFile(fileName) { 1352*333d2b36SAndroid Build Coastguard Worker items.FileNames[writeIndex] = fileName 1353*333d2b36SAndroid Build Coastguard Worker writeIndex++ 1354*333d2b36SAndroid Build Coastguard Worker } 1355*333d2b36SAndroid Build Coastguard Worker } 1356*333d2b36SAndroid Build Coastguard Worker // resize 1357*333d2b36SAndroid Build Coastguard Worker items.FileNames = items.FileNames[:writeIndex] 1358*333d2b36SAndroid Build Coastguard Worker 1359*333d2b36SAndroid Build Coastguard Worker writeIndex = 0 1360*333d2b36SAndroid Build Coastguard Worker for _, dirName := range items.DirNames { 1361*333d2b36SAndroid Build Coastguard Worker items.DirNames[writeIndex] = dirName 1362*333d2b36SAndroid Build Coastguard Worker // ignore other dirs that are known to not be inputs to the build process 1363*333d2b36SAndroid Build Coastguard Worker include := true 1364*333d2b36SAndroid Build Coastguard Worker for _, excludedName := range f.cacheMetadata.Config.ExcludeDirs { 1365*333d2b36SAndroid Build Coastguard Worker if dirName == excludedName { 1366*333d2b36SAndroid Build Coastguard Worker // don't include 1367*333d2b36SAndroid Build Coastguard Worker include = false 1368*333d2b36SAndroid Build Coastguard Worker break 1369*333d2b36SAndroid Build Coastguard Worker } 1370*333d2b36SAndroid Build Coastguard Worker } 1371*333d2b36SAndroid Build Coastguard Worker if include { 1372*333d2b36SAndroid Build Coastguard Worker writeIndex++ 1373*333d2b36SAndroid Build Coastguard Worker } 1374*333d2b36SAndroid Build Coastguard Worker } 1375*333d2b36SAndroid Build Coastguard Worker // resize 1376*333d2b36SAndroid Build Coastguard Worker items.DirNames = items.DirNames[:writeIndex] 1377*333d2b36SAndroid Build Coastguard Worker} 1378*333d2b36SAndroid Build Coastguard Worker 1379*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) listDirsAsync(nodes []*pathMap) { 1380*333d2b36SAndroid Build Coastguard Worker f.threadPool.Run( 1381*333d2b36SAndroid Build Coastguard Worker func() { 1382*333d2b36SAndroid Build Coastguard Worker for i := range nodes { 1383*333d2b36SAndroid Build Coastguard Worker f.listDirSync(nodes[i]) 1384*333d2b36SAndroid Build Coastguard Worker } 1385*333d2b36SAndroid Build Coastguard Worker }, 1386*333d2b36SAndroid Build Coastguard Worker ) 1387*333d2b36SAndroid Build Coastguard Worker} 1388*333d2b36SAndroid Build Coastguard Worker 1389*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) listDirAsync(node *pathMap) { 1390*333d2b36SAndroid Build Coastguard Worker f.threadPool.Run( 1391*333d2b36SAndroid Build Coastguard Worker func() { 1392*333d2b36SAndroid Build Coastguard Worker f.listDirSync(node) 1393*333d2b36SAndroid Build Coastguard Worker }, 1394*333d2b36SAndroid Build Coastguard Worker ) 1395*333d2b36SAndroid Build Coastguard Worker} 1396*333d2b36SAndroid Build Coastguard Worker 1397*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) listDirSync(dir *pathMap) { 1398*333d2b36SAndroid Build Coastguard Worker path := dir.path 1399*333d2b36SAndroid Build Coastguard Worker children, err := f.filesystem.ReadDir(path) 1400*333d2b36SAndroid Build Coastguard Worker 1401*333d2b36SAndroid Build Coastguard Worker if err != nil { 1402*333d2b36SAndroid Build Coastguard Worker // possibly record this error 1403*333d2b36SAndroid Build Coastguard Worker f.onFsError(path, err) 1404*333d2b36SAndroid Build Coastguard Worker // if listing the contents of the directory fails (presumably due to 1405*333d2b36SAndroid Build Coastguard Worker // permission denied), then treat the directory as empty 1406*333d2b36SAndroid Build Coastguard Worker children = nil 1407*333d2b36SAndroid Build Coastguard Worker } 1408*333d2b36SAndroid Build Coastguard Worker 1409*333d2b36SAndroid Build Coastguard Worker var subdirs []string 1410*333d2b36SAndroid Build Coastguard Worker var subfiles []string 1411*333d2b36SAndroid Build Coastguard Worker 1412*333d2b36SAndroid Build Coastguard Worker for _, child := range children { 1413*333d2b36SAndroid Build Coastguard Worker linkBits := child.Mode() & os.ModeSymlink 1414*333d2b36SAndroid Build Coastguard Worker isLink := linkBits != 0 1415*333d2b36SAndroid Build Coastguard Worker if isLink { 1416*333d2b36SAndroid Build Coastguard Worker childPath := filepath.Join(path, child.Name()) 1417*333d2b36SAndroid Build Coastguard Worker childStat, err := f.filesystem.Stat(childPath) 1418*333d2b36SAndroid Build Coastguard Worker if err != nil { 1419*333d2b36SAndroid Build Coastguard Worker // If stat fails this is probably a broken or dangling symlink, treat it as a file. 1420*333d2b36SAndroid Build Coastguard Worker subfiles = append(subfiles, child.Name()) 1421*333d2b36SAndroid Build Coastguard Worker } else if childStat.IsDir() { 1422*333d2b36SAndroid Build Coastguard Worker // Skip symlink dirs if not requested otherwise. Android has a number 1423*333d2b36SAndroid Build Coastguard Worker // of symlinks creating infinite source trees which would otherwise get 1424*333d2b36SAndroid Build Coastguard Worker // us in an infinite loop. 1425*333d2b36SAndroid Build Coastguard Worker // TODO(b/197349722): Revisit this once symlink loops are banned in the 1426*333d2b36SAndroid Build Coastguard Worker // source tree. 1427*333d2b36SAndroid Build Coastguard Worker if f.cacheMetadata.Config.FollowSymlinks { 1428*333d2b36SAndroid Build Coastguard Worker subdirs = append(subdirs, child.Name()) 1429*333d2b36SAndroid Build Coastguard Worker } 1430*333d2b36SAndroid Build Coastguard Worker } else { 1431*333d2b36SAndroid Build Coastguard Worker // We do have to support symlink files because the link name might be 1432*333d2b36SAndroid Build Coastguard Worker // different than the target name 1433*333d2b36SAndroid Build Coastguard Worker // (for example, Android.bp -> build/soong/root.bp) 1434*333d2b36SAndroid Build Coastguard Worker subfiles = append(subfiles, child.Name()) 1435*333d2b36SAndroid Build Coastguard Worker } 1436*333d2b36SAndroid Build Coastguard Worker } else if child.IsDir() { 1437*333d2b36SAndroid Build Coastguard Worker subdirs = append(subdirs, child.Name()) 1438*333d2b36SAndroid Build Coastguard Worker } else { 1439*333d2b36SAndroid Build Coastguard Worker subfiles = append(subfiles, child.Name()) 1440*333d2b36SAndroid Build Coastguard Worker } 1441*333d2b36SAndroid Build Coastguard Worker 1442*333d2b36SAndroid Build Coastguard Worker } 1443*333d2b36SAndroid Build Coastguard Worker parentNode := dir 1444*333d2b36SAndroid Build Coastguard Worker 1445*333d2b36SAndroid Build Coastguard Worker entry := &DirEntries{Path: path, DirNames: subdirs, FileNames: subfiles} 1446*333d2b36SAndroid Build Coastguard Worker f.pruneCacheCandidates(entry) 1447*333d2b36SAndroid Build Coastguard Worker 1448*333d2b36SAndroid Build Coastguard Worker // create a pathMap node for each relevant subdirectory 1449*333d2b36SAndroid Build Coastguard Worker relevantChildren := map[string]*pathMap{} 1450*333d2b36SAndroid Build Coastguard Worker for _, subdirName := range entry.DirNames { 1451*333d2b36SAndroid Build Coastguard Worker childNode, found := parentNode.children[subdirName] 1452*333d2b36SAndroid Build Coastguard Worker // if we already knew of this directory, then we already have a request pending to Stat it 1453*333d2b36SAndroid Build Coastguard Worker // if we didn't already know of this directory, then we must Stat it now 1454*333d2b36SAndroid Build Coastguard Worker if !found { 1455*333d2b36SAndroid Build Coastguard Worker childNode = parentNode.newChild(subdirName) 1456*333d2b36SAndroid Build Coastguard Worker f.statDirAsync(childNode) 1457*333d2b36SAndroid Build Coastguard Worker } 1458*333d2b36SAndroid Build Coastguard Worker relevantChildren[subdirName] = childNode 1459*333d2b36SAndroid Build Coastguard Worker } 1460*333d2b36SAndroid Build Coastguard Worker // Note that in rare cases, it's possible that we're reducing the set of 1461*333d2b36SAndroid Build Coastguard Worker // children via this statement, if these are all true: 1462*333d2b36SAndroid Build Coastguard Worker // 1. we previously had a cache that knew about subdirectories of parentNode 1463*333d2b36SAndroid Build Coastguard Worker // 2. the user created a prune-file (described in pruneCacheCandidates) 1464*333d2b36SAndroid Build Coastguard Worker // inside <parentNode>, which specifies that the contents of parentNode 1465*333d2b36SAndroid Build Coastguard Worker // are to be ignored. 1466*333d2b36SAndroid Build Coastguard Worker // The fact that it's possible to remove children here means that *pathMap structs 1467*333d2b36SAndroid Build Coastguard Worker // must not be looked up from f.nodes by filepath (and instead must be accessed by 1468*333d2b36SAndroid Build Coastguard Worker // direct pointer) until after every listDirSync completes 1469*333d2b36SAndroid Build Coastguard Worker parentNode.FileNames = entry.FileNames 1470*333d2b36SAndroid Build Coastguard Worker parentNode.children = relevantChildren 1471*333d2b36SAndroid Build Coastguard Worker 1472*333d2b36SAndroid Build Coastguard Worker} 1473*333d2b36SAndroid Build Coastguard Worker 1474*333d2b36SAndroid Build Coastguard Worker// listMatches takes a node and a function that specifies which subdirectories and 1475*333d2b36SAndroid Build Coastguard Worker// files to include, and listMatches returns the matches 1476*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) listMatches(node *pathMap, 1477*333d2b36SAndroid Build Coastguard Worker filter WalkFunc) (subDirs []*pathMap, filePaths []string) { 1478*333d2b36SAndroid Build Coastguard Worker entries := DirEntries{ 1479*333d2b36SAndroid Build Coastguard Worker FileNames: node.FileNames, 1480*333d2b36SAndroid Build Coastguard Worker } 1481*333d2b36SAndroid Build Coastguard Worker entries.DirNames = make([]string, 0, len(node.children)) 1482*333d2b36SAndroid Build Coastguard Worker for childName := range node.children { 1483*333d2b36SAndroid Build Coastguard Worker entries.DirNames = append(entries.DirNames, childName) 1484*333d2b36SAndroid Build Coastguard Worker } 1485*333d2b36SAndroid Build Coastguard Worker 1486*333d2b36SAndroid Build Coastguard Worker dirNames, fileNames := filter(entries) 1487*333d2b36SAndroid Build Coastguard Worker 1488*333d2b36SAndroid Build Coastguard Worker subDirs = []*pathMap{} 1489*333d2b36SAndroid Build Coastguard Worker filePaths = make([]string, 0, len(fileNames)) 1490*333d2b36SAndroid Build Coastguard Worker for _, fileName := range fileNames { 1491*333d2b36SAndroid Build Coastguard Worker filePaths = append(filePaths, joinCleanPaths(node.path, fileName)) 1492*333d2b36SAndroid Build Coastguard Worker } 1493*333d2b36SAndroid Build Coastguard Worker subDirs = make([]*pathMap, 0, len(dirNames)) 1494*333d2b36SAndroid Build Coastguard Worker for _, childName := range dirNames { 1495*333d2b36SAndroid Build Coastguard Worker child, ok := node.children[childName] 1496*333d2b36SAndroid Build Coastguard Worker if ok { 1497*333d2b36SAndroid Build Coastguard Worker subDirs = append(subDirs, child) 1498*333d2b36SAndroid Build Coastguard Worker } 1499*333d2b36SAndroid Build Coastguard Worker } 1500*333d2b36SAndroid Build Coastguard Worker 1501*333d2b36SAndroid Build Coastguard Worker return subDirs, filePaths 1502*333d2b36SAndroid Build Coastguard Worker} 1503*333d2b36SAndroid Build Coastguard Worker 1504*333d2b36SAndroid Build Coastguard Worker// findInCacheMultithreaded spawns potentially multiple goroutines with which to search the cache. 1505*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) findInCacheMultithreaded(node *pathMap, filter WalkFunc, 1506*333d2b36SAndroid Build Coastguard Worker approxNumThreads int) []string { 1507*333d2b36SAndroid Build Coastguard Worker 1508*333d2b36SAndroid Build Coastguard Worker if approxNumThreads < 2 { 1509*333d2b36SAndroid Build Coastguard Worker // Done spawning threads; process remaining directories 1510*333d2b36SAndroid Build Coastguard Worker return f.findInCacheSinglethreaded(node, filter) 1511*333d2b36SAndroid Build Coastguard Worker } 1512*333d2b36SAndroid Build Coastguard Worker 1513*333d2b36SAndroid Build Coastguard Worker totalWork := 0 1514*333d2b36SAndroid Build Coastguard Worker for _, child := range node.children { 1515*333d2b36SAndroid Build Coastguard Worker totalWork += child.approximateNumDescendents 1516*333d2b36SAndroid Build Coastguard Worker } 1517*333d2b36SAndroid Build Coastguard Worker childrenResults := make(chan []string, len(node.children)) 1518*333d2b36SAndroid Build Coastguard Worker 1519*333d2b36SAndroid Build Coastguard Worker subDirs, filePaths := f.listMatches(node, filter) 1520*333d2b36SAndroid Build Coastguard Worker 1521*333d2b36SAndroid Build Coastguard Worker // process child directories 1522*333d2b36SAndroid Build Coastguard Worker for _, child := range subDirs { 1523*333d2b36SAndroid Build Coastguard Worker numChildThreads := approxNumThreads * child.approximateNumDescendents / totalWork 1524*333d2b36SAndroid Build Coastguard Worker childProcessor := func(child *pathMap) { 1525*333d2b36SAndroid Build Coastguard Worker childResults := f.findInCacheMultithreaded(child, filter, numChildThreads) 1526*333d2b36SAndroid Build Coastguard Worker childrenResults <- childResults 1527*333d2b36SAndroid Build Coastguard Worker } 1528*333d2b36SAndroid Build Coastguard Worker // If we're allowed to use more than 1 thread to process this directory, 1529*333d2b36SAndroid Build Coastguard Worker // then instead we use 1 thread for each subdirectory. 1530*333d2b36SAndroid Build Coastguard Worker // It would be strange to spawn threads for only some subdirectories. 1531*333d2b36SAndroid Build Coastguard Worker go childProcessor(child) 1532*333d2b36SAndroid Build Coastguard Worker } 1533*333d2b36SAndroid Build Coastguard Worker 1534*333d2b36SAndroid Build Coastguard Worker // collect results 1535*333d2b36SAndroid Build Coastguard Worker for i := 0; i < len(subDirs); i++ { 1536*333d2b36SAndroid Build Coastguard Worker childResults := <-childrenResults 1537*333d2b36SAndroid Build Coastguard Worker filePaths = append(filePaths, childResults...) 1538*333d2b36SAndroid Build Coastguard Worker } 1539*333d2b36SAndroid Build Coastguard Worker close(childrenResults) 1540*333d2b36SAndroid Build Coastguard Worker 1541*333d2b36SAndroid Build Coastguard Worker return filePaths 1542*333d2b36SAndroid Build Coastguard Worker} 1543*333d2b36SAndroid Build Coastguard Worker 1544*333d2b36SAndroid Build Coastguard Worker// findInCacheSinglethreaded synchronously searches the cache for all matching file paths 1545*333d2b36SAndroid Build Coastguard Worker// note findInCacheSinglethreaded runs 2X to 4X as fast by being iterative rather than recursive 1546*333d2b36SAndroid Build Coastguard Workerfunc (f *Finder) findInCacheSinglethreaded(node *pathMap, filter WalkFunc) []string { 1547*333d2b36SAndroid Build Coastguard Worker if node == nil { 1548*333d2b36SAndroid Build Coastguard Worker return []string{} 1549*333d2b36SAndroid Build Coastguard Worker } 1550*333d2b36SAndroid Build Coastguard Worker 1551*333d2b36SAndroid Build Coastguard Worker nodes := []*pathMap{node} 1552*333d2b36SAndroid Build Coastguard Worker matches := []string{} 1553*333d2b36SAndroid Build Coastguard Worker 1554*333d2b36SAndroid Build Coastguard Worker for len(nodes) > 0 { 1555*333d2b36SAndroid Build Coastguard Worker currentNode := nodes[0] 1556*333d2b36SAndroid Build Coastguard Worker nodes = nodes[1:] 1557*333d2b36SAndroid Build Coastguard Worker 1558*333d2b36SAndroid Build Coastguard Worker subDirs, filePaths := f.listMatches(currentNode, filter) 1559*333d2b36SAndroid Build Coastguard Worker 1560*333d2b36SAndroid Build Coastguard Worker nodes = append(nodes, subDirs...) 1561*333d2b36SAndroid Build Coastguard Worker 1562*333d2b36SAndroid Build Coastguard Worker matches = append(matches, filePaths...) 1563*333d2b36SAndroid Build Coastguard Worker } 1564*333d2b36SAndroid Build Coastguard Worker return matches 1565*333d2b36SAndroid Build Coastguard Worker} 1566