Update threading in AppInfoWithLiveness

This is the last usage of futures outside the threading module
structures.

Bug: b/304992619
Change-Id: Ie9e16321461c9a07a69ab3009b9a37936cd822d3
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 56c1ea4..43ce750 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -64,11 +64,11 @@
 import com.android.tools.r8.repackaging.RepackagingUtils;
 import com.android.tools.r8.shaking.KeepInfo.Joiner;
 import com.android.tools.r8.synthesis.CommittedItems;
+import com.android.tools.r8.threading.TaskCollection;
 import com.android.tools.r8.utils.CollectionUtils;
 import com.android.tools.r8.utils.InternalOptions;
 import com.android.tools.r8.utils.ListUtils;
 import com.android.tools.r8.utils.PredicateSet;
-import com.android.tools.r8.utils.ThreadUtils;
 import com.android.tools.r8.utils.Timing;
 import com.android.tools.r8.utils.Visibility;
 import com.android.tools.r8.utils.WorkList;
@@ -80,7 +80,6 @@
 import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
 import it.unimi.dsi.fastutil.objects.Object2BooleanMap;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Deque;
@@ -89,7 +88,6 @@
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
@@ -322,49 +320,46 @@
   }
 
   private AppInfoWithLiveness(
-      AppInfoWithLiveness previous,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
+      AppInfoWithLiveness previous, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
     this(
         previous.getSyntheticItems().commitPrunedItems(prunedItems),
         previous.getClassToFeatureSplitMap().withoutPrunedItems(prunedItems),
         previous.getMainDexInfo().withoutPrunedItems(prunedItems),
         previous.getMissingClasses(),
         previous.deadProtoTypes,
-        pruneClasses(previous.liveTypes, prunedItems, executorService, futures),
-        pruneMethods(previous.targetedMethods, prunedItems, executorService, futures),
-        pruneClasses(previous.failedClassResolutionTargets, prunedItems, executorService, futures),
-        pruneMethods(previous.failedMethodResolutionTargets, prunedItems, executorService, futures),
-        pruneFields(previous.failedFieldResolutionTargets, prunedItems, executorService, futures),
-        pruneMethods(previous.bootstrapMethods, prunedItems, executorService, futures),
-        pruneMethods(
-            previous.virtualMethodsTargetedByInvokeDirect, prunedItems, executorService, futures),
-        pruneMethods(previous.liveMethods, prunedItems, executorService, futures),
+        pruneClasses(previous.liveTypes, prunedItems, tasks),
+        pruneMethods(previous.targetedMethods, prunedItems, tasks),
+        pruneClasses(previous.failedClassResolutionTargets, prunedItems, tasks),
+        pruneMethods(previous.failedMethodResolutionTargets, prunedItems, tasks),
+        pruneFields(previous.failedFieldResolutionTargets, prunedItems, tasks),
+        pruneMethods(previous.bootstrapMethods, prunedItems, tasks),
+        pruneMethods(previous.virtualMethodsTargetedByInvokeDirect, prunedItems, tasks),
+        pruneMethods(previous.liveMethods, prunedItems, tasks),
         previous.fieldAccessInfoCollection,
         previous.methodAccessInfoCollection.withoutPrunedItems(prunedItems),
         previous.objectAllocationInfoCollection.withoutPrunedItems(prunedItems),
         pruneCallSites(previous.callSites, prunedItems),
         extendPinnedItems(previous, prunedItems.getAdditionalPinnedItems()),
         previous.mayHaveSideEffects,
-        pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
-        pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
-        pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
-        pruneMethods(previous.reprocess, prunedItems, executorService, futures),
-        pruneMethods(previous.neverReprocess, prunedItems, executorService, futures),
+        pruneMethods(previous.alwaysInline, prunedItems, tasks),
+        pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, tasks),
+        pruneMethods(previous.whyAreYouNotInlining, prunedItems, tasks),
+        pruneMethods(previous.reprocess, prunedItems, tasks),
+        pruneMethods(previous.neverReprocess, prunedItems, tasks),
         previous.alwaysClassInline,
-        pruneClasses(previous.neverClassInline, prunedItems, executorService, futures),
-        pruneClasses(previous.noClassMerging, prunedItems, executorService, futures),
-        pruneClasses(previous.noVerticalClassMerging, prunedItems, executorService, futures),
-        pruneClasses(previous.noHorizontalClassMerging, prunedItems, executorService, futures),
-        pruneMembers(previous.neverPropagateValue, prunedItems, executorService, futures),
-        pruneMapFromMembers(previous.identifierNameStrings, prunedItems, executorService, futures),
+        pruneClasses(previous.neverClassInline, prunedItems, tasks),
+        pruneClasses(previous.noClassMerging, prunedItems, tasks),
+        pruneClasses(previous.noVerticalClassMerging, prunedItems, tasks),
+        pruneClasses(previous.noHorizontalClassMerging, prunedItems, tasks),
+        pruneMembers(previous.neverPropagateValue, prunedItems, tasks),
+        pruneMapFromMembers(previous.identifierNameStrings, prunedItems, tasks),
         prunedItems.hasRemovedClasses()
             ? CollectionUtils.mergeSets(previous.prunedTypes, prunedItems.getRemovedClasses())
             : previous.prunedTypes,
         previous.switchMaps,
-        pruneClasses(previous.lockCandidates, prunedItems, executorService, futures),
-        pruneMapFromClasses(previous.initClassReferences, prunedItems, executorService, futures),
+        pruneClasses(previous.lockCandidates, prunedItems, tasks),
+        pruneMapFromClasses(previous.initClassReferences, prunedItems, tasks),
         previous.recordFieldValuesReferences);
   }
 
@@ -386,114 +381,91 @@
   }
 
   private static Set<DexType> pruneClasses(
-      Set<DexType> methods,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
-    return pruneItems(methods, prunedItems.getRemovedClasses(), executorService, futures);
+      Set<DexType> methods, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
+    return pruneItems(methods, prunedItems.getRemovedClasses(), tasks);
   }
 
   private static Set<DexField> pruneFields(
-      Set<DexField> fields,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
-    return pruneItems(fields, prunedItems.getRemovedFields(), executorService, futures);
+      Set<DexField> fields, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
+    return pruneItems(fields, prunedItems.getRemovedFields(), tasks);
   }
 
   private static Set<DexMember<?, ?>> pruneMembers(
-      Set<DexMember<?, ?>> members,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
+      Set<DexMember<?, ?>> members, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
     if (prunedItems.hasRemovedMembers()) {
-      futures.add(
-          ThreadUtils.processAsynchronously(
-              () -> {
-                Set<DexField> removedFields = prunedItems.getRemovedFields();
-                Set<DexMethod> removedMethods = prunedItems.getRemovedMethods();
-                if (members.size() <= removedFields.size() + removedMethods.size()) {
-                  members.removeIf(
-                      member ->
-                          member.isDexField()
-                              ? removedFields.contains(member.asDexField())
-                              : removedMethods.contains(member.asDexMethod()));
-                } else {
-                  removedFields.forEach(members::remove);
-                  removedMethods.forEach(members::remove);
-                }
-              },
-              executorService));
+      tasks.submit(
+          () -> {
+            Set<DexField> removedFields = prunedItems.getRemovedFields();
+            Set<DexMethod> removedMethods = prunedItems.getRemovedMethods();
+            if (members.size() <= removedFields.size() + removedMethods.size()) {
+              members.removeIf(
+                  member ->
+                      member.isDexField()
+                          ? removedFields.contains(member.asDexField())
+                          : removedMethods.contains(member.asDexMethod()));
+            } else {
+              removedFields.forEach(members::remove);
+              removedMethods.forEach(members::remove);
+            }
+          });
     }
     return members;
   }
 
   private static Set<DexMethod> pruneMethods(
-      Set<DexMethod> methods,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
-    return pruneItems(methods, prunedItems.getRemovedMethods(), executorService, futures);
+      Set<DexMethod> methods, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
+    return pruneItems(methods, prunedItems.getRemovedMethods(), tasks);
   }
 
-  private static <T> Set<T> pruneItems(
-      Set<T> items, Set<T> removedItems, ExecutorService executorService, List<Future<?>> futures) {
+  private static <T> Set<T> pruneItems(Set<T> items, Set<T> removedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
     if (!isThrowingSet(items) && !removedItems.isEmpty()) {
-      futures.add(
-          ThreadUtils.processAsynchronously(
-              () -> {
-                if (items.size() <= removedItems.size()) {
-                  items.removeAll(removedItems);
-                } else {
-                  removedItems.forEach(items::remove);
-                }
-              },
-              executorService));
+      tasks.submit(
+          () -> {
+            if (items.size() <= removedItems.size()) {
+              items.removeAll(removedItems);
+            } else {
+              removedItems.forEach(items::remove);
+            }
+          });
     }
     return items;
   }
 
   private static <V> Map<DexType, V> pruneMapFromClasses(
-      Map<DexType, V> map,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
-    return pruneMap(map, prunedItems.getRemovedClasses(), executorService, futures);
+      Map<DexType, V> map, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
+    return pruneMap(map, prunedItems.getRemovedClasses(), tasks);
   }
 
   private static Object2BooleanMap<DexMember<?, ?>> pruneMapFromMembers(
-      Object2BooleanMap<DexMember<?, ?>> map,
-      PrunedItems prunedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
+      Object2BooleanMap<DexMember<?, ?>> map, PrunedItems prunedItems, TaskCollection<?> tasks)
+      throws ExecutionException {
     if (prunedItems.hasRemovedMembers()) {
-      futures.add(
-          ThreadUtils.processAsynchronously(
-              () -> {
-                prunedItems.getRemovedFields().forEach(map::removeBoolean);
-                prunedItems.getRemovedMethods().forEach(map::removeBoolean);
-              },
-              executorService));
+      tasks.submit(
+          () -> {
+            prunedItems.getRemovedFields().forEach(map::removeBoolean);
+            prunedItems.getRemovedMethods().forEach(map::removeBoolean);
+          });
     }
     return map;
   }
 
   private static <K, V> Map<K, V> pruneMap(
-      Map<K, V> map,
-      Set<K> removedItems,
-      ExecutorService executorService,
-      List<Future<?>> futures) {
+      Map<K, V> map, Set<K> removedItems, TaskCollection<?> tasks) throws ExecutionException {
     if (!removedItems.isEmpty()) {
-      futures.add(
-          ThreadUtils.processAsynchronously(
-              () -> {
-                if (map.size() <= removedItems.size()) {
-                  map.keySet().removeAll(removedItems);
-                } else {
-                  removedItems.forEach(map::remove);
-                }
-              },
-              executorService));
+      tasks.submit(
+          () -> {
+            if (map.size() <= removedItems.size()) {
+              map.keySet().removeAll(removedItems);
+            } else {
+              removedItems.forEach(map::remove);
+            }
+          });
     }
     return map;
   }
@@ -1183,10 +1155,9 @@
     } else if (prunedItems.hasRemovedMembers()) {
       keepInfo.mutate(keepInfo -> keepInfo.removeKeepInfoForPrunedItems(prunedItems));
     }
-    List<Future<?>> futures = new ArrayList<>();
-    AppInfoWithLiveness appInfoWithLiveness =
-        new AppInfoWithLiveness(this, prunedItems, executorService, futures);
-    ThreadUtils.awaitFutures(futures);
+    TaskCollection<?> tasks = new TaskCollection<>(options(), executorService);
+    AppInfoWithLiveness appInfoWithLiveness = new AppInfoWithLiveness(this, prunedItems, tasks);
+    tasks.await();
     timing.end();
     return appInfoWithLiveness;
   }
diff --git a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
index 9c8f6e7..8a28293 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -9,14 +9,11 @@
 import com.android.tools.r8.threading.ThreadingModule;
 import com.android.tools.r8.utils.ListUtils.ReferenceAndIntConsumer;
 import java.util.Collection;
-import java.util.Iterator;
 import java.util.Map;
-import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ForkJoinPool;
-import java.util.concurrent.Future;
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
@@ -51,26 +48,6 @@
 
   public static final int NOT_SPECIFIED = -1;
 
-  public static <T> Future<T> processAsynchronously(
-      Action action, ExecutorService executorService) {
-    return processAsynchronously(
-        () -> {
-          action.execute();
-          return null;
-        },
-        executorService);
-  }
-
-  public static <T> void processAsynchronously(
-      Action action, ExecutorService executorService, Collection<Future<T>> futures) {
-    futures.add(processAsynchronously(action, executorService));
-  }
-
-  public static <T> Future<T> processAsynchronously(
-      Callable<T> callable, ExecutorService executorService) {
-    return executorService.submit(callable);
-  }
-
   public static <T, R, E extends Exception> Collection<R> processItemsWithResults(
       Iterable<T> items,
       ThrowingFunction<T, R, E> consumer,
@@ -237,29 +214,6 @@
         executorService);
   }
 
-  public static void awaitFutures(Iterable<? extends Future<?>> futures)
-      throws ExecutionException {
-    Iterator<? extends Future<?>> futureIterator = futures.iterator();
-    try {
-      while (futureIterator.hasNext()) {
-        futureIterator.next().get();
-      }
-    } catch (InterruptedException e) {
-      throw new RuntimeException("Interrupted while waiting for future.", e);
-    } finally {
-      // In case we get interrupted or one of the threads throws an exception, still wait for all
-      // further work to make sure synchronization guarantees are met. Calling cancel unfortunately
-      // does not guarantee that the task at hand actually terminates before cancel returns.
-      while (futureIterator.hasNext()) {
-        try {
-          futureIterator.next().get();
-        } catch (Throwable t) {
-          // Ignore any new Exception.
-        }
-      }
-    }
-  }
-
   static ExecutorService getExecutorServiceForProcessors(int processors) {
     // This heuristic is based on measurements on a 32 core (hyper-threaded) machine.
     int threads = processors <= 2 ? processors : (int) Math.ceil(Integer.min(processors, 16) / 2.0);
diff --git a/src/main/java/com/android/tools/r8/utils/threads/FutureBox.java b/src/main/java/com/android/tools/r8/utils/threads/FutureBox.java
deleted file mode 100644
index c69c7fa..0000000
--- a/src/main/java/com/android/tools/r8/utils/threads/FutureBox.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright (c) 2023, the R8 project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-package com.android.tools.r8.utils.threads;
-
-import com.android.tools.r8.errors.Unimplemented;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-
-public class FutureBox<T> implements Future<T> {
-
-  private final T value;
-
-  public FutureBox(T value) {
-    this.value = value;
-  }
-
-  @Override
-  public boolean cancel(boolean mayInterruptIfRunning) {
-    throw new Unimplemented();
-  }
-
-  @Override
-  public boolean isCancelled() {
-    throw new Unimplemented();
-  }
-
-  @Override
-  public boolean isDone() {
-    throw new Unimplemented();
-  }
-
-  @Override
-  public T get() {
-    return value;
-  }
-
-  @Override
-  public T get(long timeout, TimeUnit unit) {
-    return value;
-  }
-}