In ThreadUtils.awaitFutures, cancel remaining threads if one throws.

Bug:
Change-Id: I6887e88117bd3ecc254200686fe514ba3d736d69
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 8fe3af6..59abfdb 100644
--- a/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
+++ b/src/main/java/com/android/tools/r8/utils/ThreadUtils.java
@@ -3,9 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.utils;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
+import java.util.Iterator;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -13,17 +11,26 @@
 
 public class ThreadUtils {
 
-  public static <T> List<T> awaitFutures(Collection<? extends Future<? extends T>> futures)
+  public static void awaitFutures(Iterable<? extends Future<?>> futures)
       throws ExecutionException {
-    ArrayList<T> result = new ArrayList<>(futures.size());
-    for (Future<? extends T> f : futures) {
+    Iterator<? extends Future<?>> it = futures.iterator();
+    try {
+      while (it.hasNext()) {
+        it.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, abort all further
+      // work, if possible.
       try {
-        result.add(f.get());
-      } catch (InterruptedException e) {
-        throw new RuntimeException("Interrupted while waiting for future.", e);
+        while (it.hasNext()) {
+          it.next().cancel(true);
+        }
+      } catch (Throwable t) {
+        // Ignore the new Exception.
       }
     }
-    return result;
   }
 
   public static ExecutorService getExecutorService(int threads) {