xref: /aosp_15_r20/build/soong/finder/finder.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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