Permit use of ForkJoinQuiescingExecutor for invalidation

Changes InvalidatingNodeVisitor's use of AbstractQueueVisitor from
inheritance to composition. This allows it to vary its executor
implementation. A ForkJoinPool-specialized implementation option is
provided.

--
MOS_MIGRATED_REVID=106960011
diff --git a/src/main/java/com/google/devtools/build/skyframe/EagerInvalidator.java b/src/main/java/com/google/devtools/build/skyframe/EagerInvalidator.java
index ce76bc6..cb2a8cc 100644
--- a/src/main/java/com/google/devtools/build/skyframe/EagerInvalidator.java
+++ b/src/main/java/com/google/devtools/build/skyframe/EagerInvalidator.java
@@ -21,6 +21,7 @@
 import com.google.devtools.build.skyframe.InvalidatingNodeVisitor.InvalidationState;
 
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ForkJoinPool;
 
 import javax.annotation.Nullable;
 
@@ -80,9 +81,24 @@
             executorFactory);
   }
 
+  @Nullable
+  private static DirtyingNodeVisitor createInvalidatingVisitorIfNeeded(
+      ThinNodeQueryableGraph graph,
+      Iterable<SkyKey> diff,
+      EvaluationProgressReceiver invalidationReceiver,
+      InvalidationState state,
+      DirtyKeyTracker dirtyKeyTracker,
+      ForkJoinPool forkJoinPool) {
+    state.update(diff);
+    return state.isEmpty()
+        ? null
+        : new DirtyingNodeVisitor(
+            graph, invalidationReceiver, state, dirtyKeyTracker, forkJoinPool);
+  }
+
   /**
-   * Invalidates given values and their upward transitive closure in the graph, using an executor
-   * constructed with the provided factory, if necessary.
+   * Invalidates given values and their upward transitive closure in the graph if necessary, using
+   * an executor constructed with the provided factory.
    */
   public static void invalidate(
       ThinNodeQueryableGraph graph,
@@ -92,10 +108,6 @@
       DirtyKeyTracker dirtyKeyTracker,
       Function<ExecutorParams, ? extends ExecutorService> executorFactory)
       throws InterruptedException {
-    // If we are invalidating, we must be in an incremental build by definition, so we must
-    // maintain a consistent graph state by traversing the graph and invalidating transitive
-    // dependencies. If edges aren't present, it would be impossible to check the dependencies of
-    // a dirty node in any case.
     DirtyingNodeVisitor visitor =
         createInvalidatingVisitorIfNeeded(
             graph, diff, invalidationReceiver, state, dirtyKeyTracker, executorFactory);
@@ -105,10 +117,33 @@
   }
 
   /**
+   * Invalidates given values and their upward transitive closure in the graph if necessary, using
+   * the provided {@link ForkJoinPool}.
+   */
+  public static void invalidate(
+      ThinNodeQueryableGraph graph,
+      Iterable<SkyKey> diff,
+      EvaluationProgressReceiver invalidationReceiver,
+      InvalidationState state,
+      DirtyKeyTracker dirtyKeyTracker,
+      ForkJoinPool forkJoinPool)
+      throws InterruptedException {
+    DirtyingNodeVisitor visitor =
+        createInvalidatingVisitorIfNeeded(
+            graph, diff, invalidationReceiver, state, dirtyKeyTracker, forkJoinPool);
+    if (visitor != null) {
+      visitor.run();
+    }
+  }
+
+  /**
    * Invalidates given values and their upward transitive closure in the graph.
    */
-  public static void invalidate(DirtiableGraph graph, Iterable<SkyKey> diff,
-      EvaluationProgressReceiver invalidationReceiver, InvalidationState state,
+  public static void invalidate(
+      DirtiableGraph graph,
+      Iterable<SkyKey> diff,
+      EvaluationProgressReceiver invalidationReceiver,
+      InvalidationState state,
       DirtyKeyTracker dirtyKeyTracker)
       throws InterruptedException {
     invalidate(graph, diff, invalidationReceiver, state, dirtyKeyTracker,