Update threading in distributor and member pool collection

This removes dead code from the member pool to avoid a dead use of
threading.

Bug: b/304992619
Change-Id: I7bcb3ff848a85ae40fb4bdbe658b51742ad6c0c3
diff --git a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
index 4d10674..dc79a46 100644
--- a/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
+++ b/src/main/java/com/android/tools/r8/dex/InheritanceClassInDexDistributor.java
@@ -10,8 +10,8 @@
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexProgramClass;
 import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.threading.TaskCollection;
 import com.android.tools.r8.utils.IntBox;
-import com.android.tools.r8.utils.ThreadUtils;
 import com.google.common.collect.Maps;
 import java.util.ArrayList;
 import java.util.BitSet;
@@ -27,7 +27,6 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
 
 /**
  * Partition classes among dex files to limit LinearAlloc usage during DexOpt.
@@ -538,12 +537,14 @@
   }
 
   private void updateGroupsNumberOfIds(List<ClassGroup> groups) {
-    Collection<Future<?>> updateIdsTasks = new ArrayList<>(groups.size());
-    for (ClassGroup group : groups) {
-      updateIdsTasks.add(executorService.submit(group::updateNumbersOfIds));
-    }
+    TaskCollection<?> updateIdsTasks =
+        new TaskCollection<>(
+            appView.options().getThreadingModule(), executorService, groups.size());
     try {
-      ThreadUtils.awaitFutures(updateIdsTasks);
+      for (ClassGroup group : groups) {
+        updateIdsTasks.submit(group::updateNumbersOfIds);
+      }
+      updateIdsTasks.await();
     } catch (ExecutionException e) {
       Throwable cause = e.getCause();
       if (cause instanceof RuntimeException) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
index bd0376b..a4ccb28 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberPoolCollection.java
@@ -6,29 +6,22 @@
 import com.android.tools.r8.graph.AppView;
 import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexMember;
-import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.threading.TaskCollection;
 import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.UncheckedExecutionException;
 import com.android.tools.r8.utils.WorkList;
 import com.google.common.base.Equivalence;
 import com.google.common.base.Equivalence.Wrapper;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Deque;
 import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
 import java.util.function.BiFunction;
-import java.util.function.Predicate;
 
 // Per-class collection of member signatures.
 public abstract class MemberPoolCollection<R extends DexMember<?, R>> {
@@ -49,36 +42,22 @@
 
   public void buildAll(ExecutorService executorService, Timing timing) throws ExecutionException {
     timing.begin("Building member pool collection");
+    // Generate a future for each class that will build the member pool collection for the
+    // corresponding class. Note that, we visit the classes using a top-down class hierarchy
+    // traversal, since this ensures that we do not visit library classes that are not
+    // reachable from any program class.
+    TaskCollection<?> tasks = new TaskCollection<>(appView.options(), executorService);
     try {
-      List<Future<?>> futures = new ArrayList<>();
-
-      // Generate a future for each class that will build the member pool collection for the
-      // corresponding class. Note that, we visit the classes using a top-down class hierarchy
-      // traversal, since this ensures that we do not visit library classes that are not
-      // reachable from any program class.
       TopDownClassHierarchyTraversal.forAllClasses(appView)
-          .visit(appView.appInfo().classes(), clazz -> submit(clazz, futures, executorService));
-      ThreadUtils.awaitFutures(futures);
-    } finally {
-      timing.end();
+          .visit(
+              appView.appInfo().classes(),
+              clazz -> tasks.submitUnchecked(() -> computeMemberPoolForClass(clazz).run()));
+      tasks.await();
+    } catch (UncheckedExecutionException e) {
+      throw e.rethrow();
     }
   }
 
-  public MemberPool<R> buildForHierarchy(
-      DexClass clazz, ExecutorService executorService, Timing timing) throws ExecutionException {
-    timing.begin("Building member pool collection");
-    try {
-      List<Future<?>> futures = new ArrayList<>();
-      submitAll(
-          getAllSuperTypesInclusive(clazz, memberPools::containsKey), futures, executorService);
-      submitAll(getAllSubTypesExclusive(clazz, memberPools::containsKey), futures, executorService);
-      ThreadUtils.awaitFutures(futures);
-    } finally {
-      timing.end();
-    }
-    return get(clazz);
-  }
-
   public boolean hasPool(DexClass clazz) {
     return memberPools.containsKey(clazz);
   }
@@ -98,66 +77,8 @@
     return false;
   }
 
-  private void submitAll(
-      Iterable<? extends DexClass> classes,
-      List<Future<?>> futures,
-      ExecutorService executorService) {
-    for (DexClass clazz : classes) {
-      submit(clazz, futures, executorService);
-    }
-  }
-
-  private void submit(DexClass clazz, List<Future<?>> futures, ExecutorService executorService) {
-    futures.add(executorService.submit(computeMemberPoolForClass(clazz)));
-  }
-
   abstract Runnable computeMemberPoolForClass(DexClass clazz);
 
-  private Set<DexClass> getAllSuperTypesInclusive(
-      DexClass subject, Predicate<DexClass> stoppingCriterion) {
-    Set<DexClass> superTypes = new HashSet<>();
-    Deque<DexClass> worklist = new ArrayDeque<>();
-    worklist.add(subject);
-    while (!worklist.isEmpty()) {
-      DexClass clazz = worklist.pop();
-      if (stoppingCriterion.test(clazz)) {
-        continue;
-      }
-      if (superTypes.add(clazz)) {
-        if (clazz.superType != null) {
-          addNonNull(worklist, appView.definitionFor(clazz.superType));
-        }
-        for (DexType interfaceType : clazz.interfaces.values) {
-          addNonNull(worklist, appView.definitionFor(interfaceType));
-        }
-      }
-    }
-    return superTypes;
-  }
-
-  private Set<DexClass> getAllSubTypesExclusive(
-      DexClass subject, Predicate<DexClass> stoppingCriterion) {
-    Set<DexClass> subTypes = new HashSet<>();
-    Deque<DexClass> worklist = new ArrayDeque<>();
-    subtypingInfo.forAllImmediateExtendsSubtypes(
-        subject.type, type -> addNonNull(worklist, appView.definitionFor(type)));
-    subtypingInfo.forAllImmediateImplementsSubtypes(
-        subject.type, type -> addNonNull(worklist, appView.definitionFor(type)));
-    while (!worklist.isEmpty()) {
-      DexClass clazz = worklist.pop();
-      if (stoppingCriterion.test(clazz)) {
-        continue;
-      }
-      if (subTypes.add(clazz)) {
-        subtypingInfo.forAllImmediateExtendsSubtypes(
-            clazz.type, type -> addNonNull(worklist, appView.definitionFor(type)));
-        subtypingInfo.forAllImmediateImplementsSubtypes(
-            clazz.type, type -> addNonNull(worklist, appView.definitionFor(type)));
-      }
-    }
-    return subTypes;
-  }
-
   public static class MemberPool<T> {
 
     private final DexClass clazz;
@@ -200,18 +121,6 @@
       return fold(member, false, true, (t, ignored) -> true);
     }
 
-    public boolean hasSeenDirectly(Wrapper<T> member) {
-      return here(member, false, (t, ignored) -> true);
-    }
-
-    public boolean hasSeenStrictlyAbove(Wrapper<T> member) {
-      return above(member, false, false, true, (t, ignored) -> true);
-    }
-
-    public boolean hasSeenStrictlyBelow(Wrapper<T> member) {
-      return below(member, false, true, (t, ignored) -> true);
-    }
-
     private <S> S above(
         Wrapper<T> member,
         boolean inclusive,
@@ -267,10 +176,4 @@
       return below(member, initialValue, terminator, accumulator);
     }
   }
-
-  private static <T> void addNonNull(Collection<T> collection, T item) {
-    if (item != null) {
-      collection.add(item);
-    }
-  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
index 78eb343..3b5d81f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MethodPoolCollection.java
@@ -12,7 +12,6 @@
 import com.android.tools.r8.graph.SubtypingInfo;
 import com.android.tools.r8.shaking.AppInfoWithLiveness;
 import com.android.tools.r8.utils.MethodSignatureEquivalence;
-import com.google.common.base.Predicates;
 import java.util.function.Predicate;
 
 // Per-class collection of method signatures.
@@ -27,10 +26,6 @@
 
   private final Predicate<DexEncodedMethod> methodTester;
 
-  public MethodPoolCollection(AppView<AppInfoWithLiveness> appView, SubtypingInfo subtypingInfo) {
-    this(appView, subtypingInfo, Predicates.alwaysTrue());
-  }
-
   public MethodPoolCollection(
       AppView<AppInfoWithLiveness> appView,
       SubtypingInfo subtypingInfo,
diff --git a/src/main/java/com/android/tools/r8/threading/TaskCollection.java b/src/main/java/com/android/tools/r8/threading/TaskCollection.java
index 11a64bb..8ef8168 100644
--- a/src/main/java/com/android/tools/r8/threading/TaskCollection.java
+++ b/src/main/java/com/android/tools/r8/threading/TaskCollection.java
@@ -6,6 +6,7 @@
 
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ThrowingAction;
+import com.android.tools.r8.utils.UncheckedExecutionException;
 import com.google.common.util.concurrent.Futures;
 import java.util.ArrayList;
 import java.util.List;
@@ -20,16 +21,21 @@
 
   private final ThreadingModule threadingModule;
   private final ExecutorService executorService;
+  private final List<Future<T>> futures;
 
-  private final List<Future<T>> futures = new ArrayList<>();
-
-  public TaskCollection(ThreadingModule threadingModule, ExecutorService executorService) {
+  public TaskCollection(
+      ThreadingModule threadingModule, ExecutorService executorService, int initialCapacity) {
     this.threadingModule = threadingModule;
     this.executorService = executorService;
+    this.futures = initialCapacity > 0 ? new ArrayList<>(initialCapacity) : new ArrayList<>();
+  }
+
+  public TaskCollection(ThreadingModule threadingModule, ExecutorService executorService) {
+    this(threadingModule, executorService, -1);
   }
 
   public TaskCollection(InternalOptions options, ExecutorService executorService) {
-    this(options.getThreadingModule(), executorService);
+    this(options.getThreadingModule(), executorService, -1);
   }
 
   /**
@@ -99,6 +105,24 @@
         });
   }
 
+  /** Derived submit to hide the execution exception */
+  public final void submitUnchecked(Callable<T> task) {
+    try {
+      submit(task);
+    } catch (ExecutionException e) {
+      throw new UncheckedExecutionException(e);
+    }
+  }
+
+  /** Derived submit to hide the execution exception */
+  public final <E extends Exception> void submitUnchecked(ThrowingAction<E> task) {
+    try {
+      submit(task);
+    } catch (ExecutionException e) {
+      throw new UncheckedExecutionException(e);
+    }
+  }
+
   /** Derived await when no results are needed. */
   public final void await() throws ExecutionException {
     await(null);