Cache all previously computed values in SkyQueryEnvironment#beforeEvaluateQuery if possible to save on latency for small queries.

This assumes that if the graph is up to date, then the data in SkyQueryEnvironment is also up to date. It also assumes that a ForkJoinPool remains usable until #shutdownNow is called.

--
MOS_MIGRATED_REVID=139386363
diff --git a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
index 1ed21a0..ad51cd0 100644
--- a/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
+++ b/src/main/java/com/google/devtools/build/lib/query2/SkyQueryEnvironment.java
@@ -136,6 +136,7 @@
   private MultisetSemaphore<PackageIdentifier> packageSemaphore;
   protected WalkableGraph graph;
   private InterruptibleSupplier<ImmutableSet<PathFragment>> blacklistPatternsSupplier;
+  private GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider;
   private ForkJoinPool forkJoinPool;
   private RecursivePackageProviderBackedTargetPatternResolver resolver;
   private final SkyKey universeKey;
@@ -203,26 +204,38 @@
   }
 
   private void beforeEvaluateQuery() throws InterruptedException {
-    EvaluationResult<SkyValue> result;
-    try (AutoProfiler p = AutoProfiler.logged("evaluation and walkable graph", LOG)) {
-      result = graphFactory.prepareAndGet(universeKey, loadingPhaseThreads, eventHandler);
+    boolean resolverNeedsRecreation = false;
+    if (graph == null || !graphFactory.isUpToDate(universeKey)) {
+      // If this environment is uninitialized or the graph factory needs to evaluate, do so. We
+      // assume here that this environment cannot be initialized-but-stale if the factory is up
+      // to date.
+      EvaluationResult<SkyValue> result;
+      try (AutoProfiler p = AutoProfiler.logged("evaluation and walkable graph", LOG)) {
+        result = graphFactory.prepareAndGet(universeKey, loadingPhaseThreads, eventHandler);
+      }
+      checkEvaluationResult(result);
+
+      packageSemaphore = makeFreshPackageMultisetSemaphore();
+      graph = result.getWalkableGraph();
+      blacklistPatternsSupplier = InterruptibleSupplier.Memoize.of(new BlacklistSupplier(graph));
+
+      graphBackedRecursivePackageProvider =
+          new GraphBackedRecursivePackageProvider(graph, universeTargetPatternKeys, pkgPath);
+      resolverNeedsRecreation = true;
     }
-    checkEvaluationResult(result);
-
-    packageSemaphore = makeFreshPackageMultisetSemaphore();
-    graph = result.getWalkableGraph();
-    blacklistPatternsSupplier = InterruptibleSupplier.Memoize.of(new BlacklistSupplier(graph));
-
-    GraphBackedRecursivePackageProvider graphBackedRecursivePackageProvider =
-        new GraphBackedRecursivePackageProvider(graph, universeTargetPatternKeys, pkgPath);
-    forkJoinPool =
-        NamedForkJoinPool.newNamedPool("QueryEnvironment", queryEvaluationParallelismLevel);
-    resolver =
-        new RecursivePackageProviderBackedTargetPatternResolver(
-            graphBackedRecursivePackageProvider,
-            eventHandler,
-            TargetPatternEvaluator.DEFAULT_FILTERING_POLICY,
-            packageSemaphore);
+    if (forkJoinPool == null) {
+      forkJoinPool =
+          NamedForkJoinPool.newNamedPool("QueryEnvironment", queryEvaluationParallelismLevel);
+      resolverNeedsRecreation = true;
+    }
+    if (resolverNeedsRecreation) {
+      resolver =
+          new RecursivePackageProviderBackedTargetPatternResolver(
+              graphBackedRecursivePackageProvider,
+              eventHandler,
+              TargetPatternEvaluator.DEFAULT_FILTERING_POLICY,
+              packageSemaphore);
+    }
   }
 
   protected MultisetSemaphore<PackageIdentifier> makeFreshPackageMultisetSemaphore() {
@@ -319,14 +332,21 @@
   protected void evalTopLevelInternal(
       QueryExpression expr, OutputFormatterCallback<Target> callback)
           throws QueryException, InterruptedException {
+    boolean poolNeedsShutdown = true;
     try {
       super.evalTopLevelInternal(expr, callback);
+      poolNeedsShutdown = false;
     } finally {
-      // Force termination of remaining tasks - if evaluateQuery was successful there should be
-      // none, if it failed abruptly (e.g. was interrupted) we don't want to leave any dangling
-      // threads running tasks.
-      forkJoinPool.shutdownNow();
+      if (poolNeedsShutdown) {
+        // Force termination of remaining tasks if evaluation failed abruptly (e.g. was
+        // interrupted). We don't want to leave any dangling threads running tasks.
+        forkJoinPool.shutdownNow();
+      }
       forkJoinPool.awaitQuiescence(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
+      if (poolNeedsShutdown) {
+        // Signal that pool must be recreated on the next invocation.
+        forkJoinPool = null;
+      }
     }
   }