Run the Finder and make its results available to Kati

The Finder runs roughly 200ms faster than findleaves.py in aosp,
and runs roughly 400ms faster in internal master.

Bug: 64363847
Test: m -j

Change-Id: I62db8dacc90871e913576fe2443021fb1749a483
diff --git a/finder/finder.go b/finder/finder.go
index f15c8c1..8f9496d 100644
--- a/finder/finder.go
+++ b/finder/finder.go
@@ -148,10 +148,11 @@
 	filesystem          fs.FileSystem
 
 	// temporary state
-	threadPool *threadPool
-	mutex      sync.Mutex
-	fsErrs     []fsErr
-	errlock    sync.Mutex
+	threadPool        *threadPool
+	mutex             sync.Mutex
+	fsErrs            []fsErr
+	errlock           sync.Mutex
+	shutdownWaitgroup sync.WaitGroup
 
 	// non-temporary state
 	modifiedFlag int32
@@ -183,6 +184,8 @@
 
 		nodes:  *newPathMap("/"),
 		DbPath: dbPath,
+
+		shutdownWaitgroup: sync.WaitGroup{},
 	}
 
 	f.loadFromFilesystem()
@@ -195,9 +198,12 @@
 
 	// confirm that every path mentioned in the CacheConfig exists
 	for _, path := range cacheParams.RootDirs {
+		if !filepath.IsAbs(path) {
+			path = filepath.Join(f.cacheMetadata.Config.WorkingDirectory, path)
+		}
 		node := f.nodes.GetNode(filepath.Clean(path), false)
 		if node == nil || node.ModTime == 0 {
-			return nil, fmt.Errorf("%v does not exist\n", path)
+			return nil, fmt.Errorf("path %v was specified to be included in the cache but does not exist\n", path)
 		}
 	}
 
@@ -310,20 +316,32 @@
 	return results
 }
 
-// Shutdown saves the contents of the Finder to its database file
+// Shutdown declares that the finder is no longer needed and waits for its cleanup to complete
+// Currently, that only entails waiting for the database dump to complete.
 func (f *Finder) Shutdown() {
-	f.verbosef("Shutting down\n")
+	f.waitForDbDump()
+}
+
+// End of public api
+
+func (f *Finder) goDumpDb() {
 	if f.wasModified() {
-		err := f.dumpDb()
-		if err != nil {
-			f.verbosef("%v\n", err)
-		}
+		f.shutdownWaitgroup.Add(1)
+		go func() {
+			err := f.dumpDb()
+			if err != nil {
+				f.verbosef("%v\n", err)
+			}
+			f.shutdownWaitgroup.Done()
+		}()
 	} else {
 		f.verbosef("Skipping dumping unmodified db\n")
 	}
 }
 
-// End of public api
+func (f *Finder) waitForDbDump() {
+	f.shutdownWaitgroup.Wait()
+}
 
 // joinCleanPaths is like filepath.Join but is faster because
 // joinCleanPaths doesn't have to support paths ending in "/" or containing ".."
@@ -353,6 +371,8 @@
 		f.startWithoutExternalCache()
 	}
 
+	f.goDumpDb()
+
 	f.threadPool = nil
 }
 
diff --git a/finder/finder_test.go b/finder/finder_test.go
index 15c3728..8d1bbd7 100644
--- a/finder/finder_test.go
+++ b/finder/finder_test.go
@@ -466,12 +466,13 @@
 	create(t, "/cwd/hi.txt", filesystem)
 	create(t, "/cwd/a/hi.txt", filesystem)
 	create(t, "/cwd/a/a/hi.txt", filesystem)
+	create(t, "/rel/a/hi.txt", filesystem)
 
 	finder := newFinder(
 		t,
 		filesystem,
 		CacheParams{
-			RootDirs:     []string{"/cwd", "/tmp/include"},
+			RootDirs:     []string{"/cwd", "../rel", "/tmp/include"},
 			IncludeFiles: []string{"hi.txt"},
 		},
 	)
@@ -491,6 +492,10 @@
 			"a/hi.txt",
 			"a/a/hi.txt"})
 
+	foundPaths = finder.FindNamedAt("/rel", "hi.txt")
+	assertSameResponse(t, foundPaths,
+		[]string{"/rel/a/hi.txt"})
+
 	foundPaths = finder.FindNamedAt("/tmp/include", "hi.txt")
 	assertSameResponse(t, foundPaths, []string{"/tmp/include/hi.txt"})
 }